include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / win32u / window.c
blobe5da6be30e0d7eb0695f9fe904e88ea97d29a892
1 /*
2 * Window related functions
4 * Copyright 1993, 1994 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #if 0
23 #pragma makedep unix
24 #endif
26 #include <assert.h>
28 #include "ntstatus.h"
29 #define WIN32_NO_STATUS
30 #include "ntgdi_private.h"
31 #include "ntuser_private.h"
32 #include "wine/server.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(win);
37 #define NB_USER_HANDLES ((LAST_USER_HANDLE - FIRST_USER_HANDLE + 1) >> 1)
38 #define USER_HANDLE_TO_INDEX(hwnd) ((LOWORD(hwnd) - FIRST_USER_HANDLE) >> 1)
40 static void *user_handles[NB_USER_HANDLES];
42 #define SWP_AGG_NOGEOMETRYCHANGE \
43 (SWP_NOSIZE | SWP_NOCLIENTSIZE | SWP_NOZORDER)
44 #define SWP_AGG_NOPOSCHANGE \
45 (SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE | SWP_NOZORDER)
46 #define SWP_AGG_STATUSFLAGS \
47 (SWP_AGG_NOPOSCHANGE | SWP_FRAMECHANGED | SWP_HIDEWINDOW | SWP_SHOWWINDOW)
48 #define SWP_AGG_NOCLIENTCHANGE \
49 (SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE)
51 #define PLACE_MIN 0x0001
52 #define PLACE_MAX 0x0002
53 #define PLACE_RECT 0x0004
55 /***********************************************************************
56 * alloc_user_handle
58 HANDLE alloc_user_handle( struct user_object *ptr, unsigned int type )
60 HANDLE handle = 0;
62 SERVER_START_REQ( alloc_user_handle )
64 if (!wine_server_call_err( req )) handle = wine_server_ptr_handle( reply->handle );
66 SERVER_END_REQ;
68 if (handle)
70 UINT index = USER_HANDLE_TO_INDEX( handle );
72 assert( index < NB_USER_HANDLES );
73 ptr->handle = handle;
74 ptr->type = type;
75 InterlockedExchangePointer( &user_handles[index], ptr );
77 return handle;
80 /***********************************************************************
81 * get_user_handle_ptr
83 void *get_user_handle_ptr( HANDLE handle, unsigned int type )
85 struct user_object *ptr;
86 WORD index = USER_HANDLE_TO_INDEX( handle );
88 if (index >= NB_USER_HANDLES) return NULL;
90 user_lock();
91 if ((ptr = user_handles[index]))
93 if (ptr->type == type &&
94 ((UINT)(UINT_PTR)ptr->handle == (UINT)(UINT_PTR)handle ||
95 !HIWORD(handle) || HIWORD(handle) == 0xffff))
96 return ptr;
97 ptr = NULL;
99 else ptr = OBJ_OTHER_PROCESS;
100 user_unlock();
101 return ptr;
104 /***********************************************************************
105 * next_process_user_handle_ptr
107 * user_lock must be held by caller.
109 void *next_process_user_handle_ptr( HANDLE *handle, unsigned int type )
111 struct user_object *ptr;
112 WORD index = *handle ? USER_HANDLE_TO_INDEX( *handle ) + 1 : 0;
114 while (index < NB_USER_HANDLES)
116 if (!(ptr = user_handles[index++])) continue; /* OBJ_OTHER_PROCESS */
117 if (ptr->type != type) continue;
118 *handle = ptr->handle;
119 return ptr;
121 return NULL;
124 /***********************************************************************
125 * set_user_handle_ptr
127 static void set_user_handle_ptr( HANDLE handle, struct user_object *ptr )
129 WORD index = USER_HANDLE_TO_INDEX(handle);
130 assert( index < NB_USER_HANDLES );
131 InterlockedExchangePointer( &user_handles[index], ptr );
134 /***********************************************************************
135 * release_user_handle_ptr
137 void release_user_handle_ptr( void *ptr )
139 assert( ptr && ptr != OBJ_OTHER_PROCESS );
140 user_unlock();
143 /***********************************************************************
144 * free_user_handle
146 void *free_user_handle( HANDLE handle, unsigned int type )
148 struct user_object *ptr;
149 WORD index = USER_HANDLE_TO_INDEX( handle );
151 if ((ptr = get_user_handle_ptr( handle, type )) && ptr != OBJ_OTHER_PROCESS)
153 SERVER_START_REQ( free_user_handle )
155 req->handle = wine_server_user_handle( handle );
156 if (wine_server_call( req )) ptr = NULL;
157 else InterlockedCompareExchangePointer( &user_handles[index], NULL, ptr );
159 SERVER_END_REQ;
160 user_unlock();
162 return ptr;
165 /*******************************************************************
166 * get_hwnd_message_parent
168 * Return the parent for HWND_MESSAGE windows.
170 HWND get_hwnd_message_parent(void)
172 struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
174 if (!thread_info->msg_window) get_desktop_window(); /* trigger creation */
175 return UlongToHandle( thread_info->msg_window );
178 /***********************************************************************
179 * get_full_window_handle
181 * Convert a possibly truncated window handle to a full 32-bit handle.
183 HWND get_full_window_handle( HWND hwnd )
185 WND *win;
187 if (!hwnd || (ULONG_PTR)hwnd >> 16) return hwnd;
188 if (LOWORD(hwnd) <= 1 || LOWORD(hwnd) == 0xffff) return hwnd;
189 /* do sign extension for -2 and -3 */
190 if (LOWORD(hwnd) >= (WORD)-3) return (HWND)(LONG_PTR)(INT16)LOWORD(hwnd);
192 if (!(win = get_win_ptr( hwnd ))) return hwnd;
194 if (win == WND_DESKTOP)
196 if (LOWORD(hwnd) == LOWORD(get_desktop_window())) return get_desktop_window();
197 else return get_hwnd_message_parent();
200 if (win != WND_OTHER_PROCESS)
202 hwnd = win->obj.handle;
203 release_win_ptr( win );
205 else /* may belong to another process */
207 SERVER_START_REQ( get_window_info )
209 req->handle = wine_server_user_handle( hwnd );
210 if (!wine_server_call_err( req )) hwnd = wine_server_ptr_handle( reply->full_handle );
212 SERVER_END_REQ;
214 return hwnd;
217 /*******************************************************************
218 * is_desktop_window
220 * Check if window is the desktop or the HWND_MESSAGE top parent.
222 BOOL is_desktop_window( HWND hwnd )
224 struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
226 if (!hwnd) return FALSE;
227 if (hwnd == UlongToHandle( thread_info->top_window )) return TRUE;
228 if (hwnd == UlongToHandle( thread_info->msg_window )) return TRUE;
230 if (!HIWORD(hwnd) || HIWORD(hwnd) == 0xffff)
232 if (LOWORD(thread_info->top_window) == LOWORD(hwnd)) return TRUE;
233 if (LOWORD(thread_info->msg_window) == LOWORD(hwnd)) return TRUE;
235 return FALSE;
238 /***********************************************************************
239 * win_get_ptr
241 * Return a pointer to the WND structure if local to the process,
242 * or WND_OTHER_PROCESS if handle may be valid in other process.
243 * If ret value is a valid pointer, it must be released with WIN_ReleasePtr.
245 WND *get_win_ptr( HWND hwnd )
247 WND *win;
249 if ((win = get_user_handle_ptr( hwnd, NTUSER_OBJ_WINDOW )) == WND_OTHER_PROCESS)
251 if (is_desktop_window( hwnd )) win = WND_DESKTOP;
253 return win;
256 /***********************************************************************
257 * is_current_thread_window
259 * Check whether a given window belongs to the current process (and return the full handle).
261 HWND is_current_thread_window( HWND hwnd )
263 WND *win;
264 HWND ret = 0;
266 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP)
267 return 0;
268 if (win->tid == GetCurrentThreadId()) ret = win->obj.handle;
269 release_win_ptr( win );
270 return ret;
273 /***********************************************************************
274 * is_current_process_window
276 * Check whether a given window belongs to the current process (and return the full handle).
278 HWND is_current_process_window( HWND hwnd )
280 WND *ptr;
281 HWND ret;
283 if (!(ptr = get_win_ptr( hwnd )) || ptr == WND_OTHER_PROCESS || ptr == WND_DESKTOP) return 0;
284 ret = ptr->obj.handle;
285 release_win_ptr( ptr );
286 return ret;
289 /* see IsWindow */
290 BOOL is_window( HWND hwnd )
292 WND *win;
293 BOOL ret;
295 if (!(win = get_win_ptr( hwnd ))) return FALSE;
296 if (win == WND_DESKTOP) return TRUE;
298 if (win != WND_OTHER_PROCESS)
300 release_win_ptr( win );
301 return TRUE;
304 /* check other processes */
305 SERVER_START_REQ( get_window_info )
307 req->handle = wine_server_user_handle( hwnd );
308 ret = !wine_server_call_err( req );
310 SERVER_END_REQ;
311 return ret;
314 /* see GetWindowThreadProcessId */
315 DWORD get_window_thread( HWND hwnd, DWORD *process )
317 WND *ptr;
318 DWORD tid = 0;
320 if (!(ptr = get_win_ptr( hwnd )))
322 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE);
323 return 0;
326 if (ptr != WND_OTHER_PROCESS && ptr != WND_DESKTOP)
328 /* got a valid window */
329 tid = ptr->tid;
330 if (process) *process = GetCurrentProcessId();
331 release_win_ptr( ptr );
332 return tid;
335 /* check other processes */
336 SERVER_START_REQ( get_window_info )
338 req->handle = wine_server_user_handle( hwnd );
339 if (!wine_server_call_err( req ))
341 tid = (DWORD)reply->tid;
342 if (process) *process = (DWORD)reply->pid;
345 SERVER_END_REQ;
346 return tid;
349 /* see GetParent */
350 HWND get_parent( HWND hwnd )
352 HWND retval = 0;
353 WND *win;
355 if (!(win = get_win_ptr( hwnd )))
357 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
358 return 0;
360 if (win == WND_DESKTOP) return 0;
361 if (win == WND_OTHER_PROCESS)
363 LONG style = get_window_long( hwnd, GWL_STYLE );
364 if (style & (WS_POPUP | WS_CHILD))
366 SERVER_START_REQ( get_window_tree )
368 req->handle = wine_server_user_handle( hwnd );
369 if (!wine_server_call_err( req ))
371 if (style & WS_POPUP) retval = wine_server_ptr_handle( reply->owner );
372 else if (style & WS_CHILD) retval = wine_server_ptr_handle( reply->parent );
375 SERVER_END_REQ;
378 else
380 if (win->dwStyle & WS_POPUP) retval = win->owner;
381 else if (win->dwStyle & WS_CHILD) retval = win->parent;
382 release_win_ptr( win );
384 return retval;
387 /*****************************************************************
388 * NtUserSetParent (win32u.@)
390 HWND WINAPI NtUserSetParent( HWND hwnd, HWND parent )
392 RECT window_rect, old_screen_rect, new_screen_rect;
393 UINT context;
394 WINDOWPOS winpos;
395 HWND full_handle;
396 HWND old_parent = 0;
397 BOOL was_visible;
398 WND *win;
399 BOOL ret;
401 TRACE("(%p %p)\n", hwnd, parent);
403 if (is_broadcast(hwnd) || is_broadcast(parent))
405 RtlSetLastWin32Error(ERROR_INVALID_PARAMETER);
406 return 0;
409 if (!parent) parent = get_desktop_window();
410 else if (parent == HWND_MESSAGE) parent = get_hwnd_message_parent();
411 else parent = get_full_window_handle( parent );
413 if (!is_window( parent ))
415 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
416 return 0;
419 /* Some applications try to set a child as a parent */
420 if (is_child( hwnd, parent ))
422 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
423 return 0;
426 if (!(full_handle = is_current_thread_window( hwnd )))
427 return UlongToHandle( send_message( hwnd, WM_WINE_SETPARENT, (WPARAM)parent, 0 ));
429 if (full_handle == parent)
431 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
432 return 0;
435 /* Windows hides the window first, then shows it again
436 * including the WM_SHOWWINDOW messages and all */
437 was_visible = NtUserShowWindow( hwnd, SW_HIDE );
439 win = get_win_ptr( hwnd );
440 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return 0;
442 get_window_rects( hwnd, COORDS_PARENT, &window_rect, NULL, get_dpi_for_window(hwnd) );
443 get_window_rects( hwnd, COORDS_SCREEN, &old_screen_rect, NULL, 0 );
445 SERVER_START_REQ( set_parent )
447 req->handle = wine_server_user_handle( hwnd );
448 req->parent = wine_server_user_handle( parent );
449 if ((ret = !wine_server_call_err( req )))
451 old_parent = wine_server_ptr_handle( reply->old_parent );
452 win->parent = parent = wine_server_ptr_handle( reply->full_parent );
453 win->dpi_context = reply->dpi_context;
457 SERVER_END_REQ;
458 release_win_ptr( win );
459 if (!ret) return 0;
461 get_window_rects( hwnd, COORDS_SCREEN, &new_screen_rect, NULL, 0 );
462 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
464 user_driver->pSetParent( full_handle, parent, old_parent );
466 winpos.hwnd = hwnd;
467 winpos.hwndInsertAfter = HWND_TOP;
468 winpos.x = window_rect.left;
469 winpos.y = window_rect.top;
470 winpos.cx = 0;
471 winpos.cy = 0;
472 winpos.flags = SWP_NOSIZE;
474 set_window_pos( &winpos, new_screen_rect.left - old_screen_rect.left,
475 new_screen_rect.top - old_screen_rect.top );
477 if (was_visible) NtUserShowWindow( hwnd, SW_SHOW );
479 set_thread_dpi_awareness_context( context );
480 return old_parent;
483 /* see GetWindow */
484 HWND get_window_relative( HWND hwnd, UINT rel )
486 HWND retval = 0;
488 if (rel == GW_OWNER) /* this one may be available locally */
490 WND *win = get_win_ptr( hwnd );
491 if (!win)
493 RtlSetLastWin32Error( ERROR_INVALID_HANDLE );
494 return 0;
496 if (win == WND_DESKTOP) return 0;
497 if (win != WND_OTHER_PROCESS)
499 retval = win->owner;
500 release_win_ptr( win );
501 return retval;
503 /* else fall through to server call */
506 SERVER_START_REQ( get_window_tree )
508 req->handle = wine_server_user_handle( hwnd );
509 if (!wine_server_call_err( req ))
511 switch(rel)
513 case GW_HWNDFIRST:
514 retval = wine_server_ptr_handle( reply->first_sibling );
515 break;
516 case GW_HWNDLAST:
517 retval = wine_server_ptr_handle( reply->last_sibling );
518 break;
519 case GW_HWNDNEXT:
520 retval = wine_server_ptr_handle( reply->next_sibling );
521 break;
522 case GW_HWNDPREV:
523 retval = wine_server_ptr_handle( reply->prev_sibling );
524 break;
525 case GW_OWNER:
526 retval = wine_server_ptr_handle( reply->owner );
527 break;
528 case GW_CHILD:
529 retval = wine_server_ptr_handle( reply->first_child );
530 break;
534 SERVER_END_REQ;
535 return retval;
538 /*******************************************************************
539 * list_window_parents
541 * Build an array of all parents of a given window, starting with
542 * the immediate parent. The array must be freed with free().
544 static HWND *list_window_parents( HWND hwnd )
546 WND *win;
547 HWND current, *list;
548 int i, pos = 0, size = 16, count;
550 if (!(list = malloc( size * sizeof(HWND) ))) return NULL;
552 current = hwnd;
553 for (;;)
555 if (!(win = get_win_ptr( current ))) goto empty;
556 if (win == WND_OTHER_PROCESS) break; /* need to do it the hard way */
557 if (win == WND_DESKTOP)
559 if (!pos) goto empty;
560 list[pos] = 0;
561 return list;
563 list[pos] = current = win->parent;
564 release_win_ptr( win );
565 if (!current) return list;
566 if (++pos == size - 1)
568 /* need to grow the list */
569 HWND *new_list = realloc( list, (size + 16) * sizeof(HWND) );
570 if (!new_list) goto empty;
571 list = new_list;
572 size += 16;
576 /* at least one parent belongs to another process, have to query the server */
578 for (;;)
580 count = 0;
581 SERVER_START_REQ( get_window_parents )
583 req->handle = wine_server_user_handle( hwnd );
584 wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
585 if (!wine_server_call( req )) count = reply->count;
587 SERVER_END_REQ;
588 if (!count) goto empty;
589 if (size > count)
591 /* start from the end since HWND is potentially larger than user_handle_t */
592 for (i = count - 1; i >= 0; i--)
593 list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
594 list[count] = 0;
595 return list;
597 free( list );
598 size = count + 1;
599 if (!(list = malloc( size * sizeof(HWND) ))) return NULL;
602 empty:
603 free( list );
604 return NULL;
607 /*******************************************************************
608 * list_window_children
610 * Build an array of the children of a given window. The array must be
611 * freed with HeapFree. Returns NULL when no windows are found.
613 HWND *list_window_children( HDESK desktop, HWND hwnd, UNICODE_STRING *class, DWORD tid )
615 HWND *list;
616 int i, size = 128;
617 ATOM atom = class ? get_int_atom_value( class ) : 0;
619 /* empty class is not the same as NULL class */
620 if (!atom && class && !class->Length) return NULL;
622 for (;;)
624 int count = 0;
626 if (!(list = malloc( size * sizeof(HWND) ))) break;
628 SERVER_START_REQ( get_window_children )
630 req->desktop = wine_server_obj_handle( desktop );
631 req->parent = wine_server_user_handle( hwnd );
632 req->tid = tid;
633 req->atom = atom;
634 if (!atom && class) wine_server_add_data( req, class->Buffer, class->Length );
635 wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
636 if (!wine_server_call( req )) count = reply->count;
638 SERVER_END_REQ;
639 if (count && count < size)
641 /* start from the end since HWND is potentially larger than user_handle_t */
642 for (i = count - 1; i >= 0; i--)
643 list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
644 list[count] = 0;
645 return list;
647 free( list );
648 if (!count) break;
649 size = count + 1; /* restart with a large enough buffer */
651 return NULL;
654 /*****************************************************************
655 * NtUserGetAncestor (win32u.@)
657 HWND WINAPI NtUserGetAncestor( HWND hwnd, UINT type )
659 HWND *list, ret = 0;
660 WND *win;
662 switch(type)
664 case GA_PARENT:
665 if (!(win = get_win_ptr( hwnd )))
667 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
668 return 0;
670 if (win == WND_DESKTOP) return 0;
671 if (win != WND_OTHER_PROCESS)
673 ret = win->parent;
674 release_win_ptr( win );
676 else /* need to query the server */
678 SERVER_START_REQ( get_window_tree )
680 req->handle = wine_server_user_handle( hwnd );
681 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->parent );
683 SERVER_END_REQ;
685 break;
687 case GA_ROOT:
688 if (!(list = list_window_parents( hwnd ))) return 0;
690 if (!list[0] || !list[1]) ret = get_full_window_handle( hwnd ); /* top-level window */
691 else
693 int count = 2;
694 while (list[count]) count++;
695 ret = list[count - 2]; /* get the one before the desktop */
697 free( list );
698 break;
700 case GA_ROOTOWNER:
701 if (is_desktop_window( hwnd )) return 0;
702 ret = get_full_window_handle( hwnd );
703 for (;;)
705 HWND parent = get_parent( ret );
706 if (!parent) break;
707 ret = parent;
709 break;
711 return ret;
714 /* see IsChild */
715 BOOL is_child( HWND parent, HWND child )
717 HWND *list;
718 int i;
719 BOOL ret = FALSE;
721 if (!(get_window_long( child, GWL_STYLE ) & WS_CHILD)) return FALSE;
722 if (!(list = list_window_parents( child ))) return FALSE;
723 parent = get_full_window_handle( parent );
724 for (i = 0; list[i]; i++)
726 if (list[i] == parent)
728 ret = list[i] && list[i+1];
729 break;
731 if (!(get_window_long( list[i], GWL_STYLE ) & WS_CHILD)) break;
733 free( list );
734 return ret;
737 /* see IsWindowVisible */
738 BOOL is_window_visible( HWND hwnd )
740 HWND *list;
741 BOOL retval = TRUE;
742 int i;
744 if (!(get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE)) return FALSE;
745 if (!(list = list_window_parents( hwnd ))) return TRUE;
746 if (list[0])
748 for (i = 0; list[i+1]; i++)
749 if (!(get_window_long( list[i], GWL_STYLE ) & WS_VISIBLE)) break;
750 retval = !list[i+1] && (list[i] == get_desktop_window()); /* top message window isn't visible */
752 free( list );
753 return retval;
756 /***********************************************************************
757 * is_window_drawable
759 * hwnd is drawable when it is visible, all parents are not
760 * minimized, and it is itself not minimized unless we are
761 * trying to draw its default class icon.
763 BOOL is_window_drawable( HWND hwnd, BOOL icon )
765 HWND *list;
766 BOOL retval = TRUE;
767 int i;
768 LONG style = get_window_long( hwnd, GWL_STYLE );
770 if (!(style & WS_VISIBLE)) return FALSE;
771 if ((style & WS_MINIMIZE) && icon && get_class_long_ptr( hwnd, GCLP_HICON, FALSE )) return FALSE;
773 if (!(list = list_window_parents( hwnd ))) return TRUE;
774 if (list[0])
776 for (i = 0; list[i+1]; i++)
777 if ((get_window_long( list[i], GWL_STYLE ) & (WS_VISIBLE|WS_MINIMIZE)) != WS_VISIBLE)
778 break;
779 retval = !list[i+1] && (list[i] == get_desktop_window()); /* top message window isn't visible */
781 free( list );
782 return retval;
785 /* see IsWindowUnicode */
786 BOOL is_window_unicode( HWND hwnd )
788 WND *win;
789 BOOL ret = FALSE;
791 if (!(win = get_win_ptr(hwnd))) return FALSE;
793 if (win == WND_DESKTOP) return TRUE;
795 if (win != WND_OTHER_PROCESS)
797 ret = (win->flags & WIN_ISUNICODE) != 0;
798 release_win_ptr( win );
800 else
802 SERVER_START_REQ( get_window_info )
804 req->handle = wine_server_user_handle( hwnd );
805 if (!wine_server_call_err( req )) ret = reply->is_unicode;
807 SERVER_END_REQ;
809 return ret;
812 /* see EnableWindow */
813 BOOL enable_window( HWND hwnd, BOOL enable )
815 BOOL ret;
817 if (is_broadcast(hwnd))
819 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
820 return FALSE;
823 TRACE( "( %p, %d )\n", hwnd, enable );
825 if (enable)
827 ret = (set_window_style( hwnd, 0, WS_DISABLED ) & WS_DISABLED) != 0;
828 if (ret) send_message( hwnd, WM_ENABLE, TRUE, 0 );
830 else
832 send_message( hwnd, WM_CANCELMODE, 0, 0 );
834 ret = (set_window_style( hwnd, WS_DISABLED, 0 ) & WS_DISABLED) != 0;
835 if (!ret)
837 if (hwnd == get_focus())
838 NtUserSetFocus( 0 ); /* A disabled window can't have the focus */
840 send_message( hwnd, WM_ENABLE, FALSE, 0 );
843 return ret;
846 /* see IsWindowEnabled */
847 BOOL is_window_enabled( HWND hwnd )
849 LONG ret;
851 RtlSetLastWin32Error( NO_ERROR );
852 ret = get_window_long( hwnd, GWL_STYLE );
853 if (!ret && RtlGetLastWin32Error() != NO_ERROR) return FALSE;
854 return !(ret & WS_DISABLED);
857 /* see GetWindowDpiAwarenessContext */
858 UINT get_window_dpi_awareness_context( HWND hwnd )
860 UINT ret = 0;
861 WND *win;
863 if (!(win = get_win_ptr( hwnd )))
865 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
866 return 0;
868 if (win == WND_DESKTOP) return NTUSER_DPI_PER_MONITOR_AWARE;
869 if (win != WND_OTHER_PROCESS)
871 ret = win->dpi_context;
872 release_win_ptr( win );
874 else
876 SERVER_START_REQ( get_window_info )
878 req->handle = wine_server_user_handle( hwnd );
879 if (!wine_server_call_err( req )) ret = reply->dpi_context;
881 SERVER_END_REQ;
883 return ret;
886 /* see GetDpiForWindow */
887 UINT get_dpi_for_window( HWND hwnd )
889 WND *win;
890 UINT context = 0;
892 if (!(win = get_win_ptr( hwnd )))
894 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
895 return 0;
897 if (win == WND_DESKTOP)
899 POINT pt = { 0, 0 };
900 return get_monitor_dpi( monitor_from_point( pt, MONITOR_DEFAULTTOPRIMARY, 0 ));
902 if (win != WND_OTHER_PROCESS)
904 context = win->dpi_context;
905 release_win_ptr( win );
907 else
909 SERVER_START_REQ( get_window_info )
911 req->handle = wine_server_user_handle( hwnd );
912 if (!wine_server_call_err( req )) context = reply->dpi_context;
914 SERVER_END_REQ;
917 if (NTUSER_DPI_CONTEXT_IS_MONITOR_AWARE( context )) return get_win_monitor_dpi( hwnd );
918 return NTUSER_DPI_CONTEXT_GET_DPI( context );
921 static LONG_PTR get_win_data( const void *ptr, UINT size )
923 if (size == sizeof(WORD))
925 WORD ret;
926 memcpy( &ret, ptr, sizeof(ret) );
927 return ret;
929 else if (size == sizeof(DWORD))
931 DWORD ret;
932 memcpy( &ret, ptr, sizeof(ret) );
933 return ret;
935 else
937 LONG_PTR ret;
938 memcpy( &ret, ptr, sizeof(ret) );
939 return ret;
943 /* helper for set_window_long */
944 static inline void set_win_data( void *ptr, LONG_PTR val, UINT size )
946 if (size == sizeof(WORD))
948 WORD newval = val;
949 memcpy( ptr, &newval, sizeof(newval) );
951 else if (size == sizeof(DWORD))
953 DWORD newval = val;
954 memcpy( ptr, &newval, sizeof(newval) );
956 else
958 memcpy( ptr, &val, sizeof(val) );
962 BOOL is_iconic( HWND hwnd )
964 return (get_window_long( hwnd, GWL_STYLE ) & WS_MINIMIZE) != 0;
967 BOOL is_zoomed( HWND hwnd )
969 return (get_window_long( hwnd, GWL_STYLE ) & WS_MAXIMIZE) != 0;
972 static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ansi )
974 LONG_PTR retval = 0;
975 WND *win;
977 if (offset == GWLP_HWNDPARENT)
979 HWND parent = NtUserGetAncestor( hwnd, GA_PARENT );
980 if (parent == get_desktop_window())
981 parent = get_window_relative( hwnd, GW_OWNER );
982 return (ULONG_PTR)parent;
985 if (!(win = get_win_ptr( hwnd )))
987 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
988 return 0;
991 if (win == WND_DESKTOP)
993 switch (offset)
995 case GWL_STYLE:
996 retval = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; /* message parent is not visible */
997 if (get_full_window_handle( hwnd ) == get_desktop_window())
998 retval |= WS_VISIBLE;
999 return retval;
1000 case GWL_EXSTYLE:
1001 case GWLP_USERDATA:
1002 case GWLP_ID:
1003 case GWLP_HINSTANCE:
1004 return 0;
1005 case GWLP_WNDPROC:
1006 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
1007 return 0;
1009 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1010 return 0;
1013 if (win == WND_OTHER_PROCESS)
1015 if (offset == GWLP_WNDPROC)
1017 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
1018 return 0;
1020 SERVER_START_REQ( set_window_info )
1022 req->handle = wine_server_user_handle( hwnd );
1023 req->flags = 0; /* don't set anything, just retrieve */
1024 req->extra_offset = (offset >= 0) ? offset : -1;
1025 req->extra_size = (offset >= 0) ? size : 0;
1026 if (!wine_server_call_err( req ))
1028 switch(offset)
1030 case GWL_STYLE: retval = reply->old_style; break;
1031 case GWL_EXSTYLE: retval = reply->old_ex_style; break;
1032 case GWLP_ID: retval = reply->old_id; break;
1033 case GWLP_HINSTANCE: retval = (ULONG_PTR)wine_server_get_ptr( reply->old_instance ); break;
1034 case GWLP_USERDATA: retval = reply->old_user_data; break;
1035 default:
1036 if (offset >= 0) retval = get_win_data( &reply->old_extra_value, size );
1037 else RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1038 break;
1042 SERVER_END_REQ;
1043 return retval;
1046 /* now we have a valid win */
1048 if (offset >= 0)
1050 if (offset > (int)(win->cbWndExtra - size))
1052 WARN("Invalid offset %d\n", offset );
1053 release_win_ptr( win );
1054 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1055 return 0;
1057 retval = get_win_data( (char *)win->wExtra + offset, size );
1058 release_win_ptr( win );
1059 return retval;
1062 switch(offset)
1064 case GWLP_USERDATA: retval = win->userdata; break;
1065 case GWL_STYLE: retval = win->dwStyle; break;
1066 case GWL_EXSTYLE: retval = win->dwExStyle; break;
1067 case GWLP_ID: retval = win->wIDmenu; break;
1068 case GWLP_HINSTANCE: retval = (ULONG_PTR)win->hInstance; break;
1069 case GWLP_WNDPROC:
1070 /* This looks like a hack only for the edit control (see tests). This makes these controls
1071 * more tolerant to A/W mismatches. The lack of W->A->W conversion for such a mismatch suggests
1072 * that the hack is in GetWindowLongPtr[AW], not in winprocs.
1074 if (win->winproc == BUILTIN_WINPROC(WINPROC_EDIT) && (!!ansi != !(win->flags & WIN_ISUNICODE)))
1075 retval = (ULONG_PTR)win->winproc;
1076 else
1077 retval = (ULONG_PTR)get_winproc( win->winproc, ansi );
1078 break;
1079 default:
1080 WARN("Unknown offset %d\n", offset );
1081 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1082 break;
1084 release_win_ptr( win );
1085 return retval;
1088 /* see GetWindowLongW */
1089 DWORD get_window_long( HWND hwnd, INT offset )
1091 return get_window_long_size( hwnd, offset, sizeof(LONG), FALSE );
1094 /* see GetWindowLongPtr */
1095 ULONG_PTR get_window_long_ptr( HWND hwnd, INT offset, BOOL ansi )
1097 return get_window_long_size( hwnd, offset, sizeof(LONG_PTR), ansi );
1100 /* see GetWindowWord */
1101 static WORD get_window_word( HWND hwnd, INT offset )
1103 if (offset < 0 && offset != GWLP_USERDATA)
1105 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1106 return 0;
1108 return get_window_long_size( hwnd, offset, sizeof(WORD), TRUE );
1111 /***********************************************************************
1112 * set_window_style
1114 * Change the style of a window.
1116 ULONG set_window_style( HWND hwnd, ULONG set_bits, ULONG clear_bits )
1118 BOOL ok, made_visible = FALSE;
1119 STYLESTRUCT style;
1120 WND *win = get_win_ptr( hwnd );
1122 if (!win || win == WND_DESKTOP) return 0;
1123 if (win == WND_OTHER_PROCESS)
1125 if (is_window(hwnd))
1126 return send_message( hwnd, WM_WINE_SETSTYLE, set_bits, clear_bits );
1127 return 0;
1129 style.styleOld = win->dwStyle;
1130 style.styleNew = (win->dwStyle | set_bits) & ~clear_bits;
1131 if (style.styleNew == style.styleOld)
1133 release_win_ptr( win );
1134 return style.styleNew;
1136 SERVER_START_REQ( set_window_info )
1138 req->handle = wine_server_user_handle( hwnd );
1139 req->flags = SET_WIN_STYLE;
1140 req->style = style.styleNew;
1141 req->extra_offset = -1;
1142 if ((ok = !wine_server_call( req )))
1144 style.styleOld = reply->old_style;
1145 win->dwStyle = style.styleNew;
1148 SERVER_END_REQ;
1150 if (ok && ((style.styleOld ^ style.styleNew) & WS_VISIBLE))
1152 made_visible = (style.styleNew & WS_VISIBLE) != 0;
1153 invalidate_dce( win, NULL );
1155 release_win_ptr( win );
1157 if (!ok) return 0;
1159 user_driver->pSetWindowStyle( hwnd, GWL_STYLE, &style );
1160 if (made_visible) update_window_state( hwnd );
1162 return style.styleOld;
1165 static DWORD fix_exstyle( DWORD style, DWORD exstyle )
1167 if ((exstyle & WS_EX_DLGMODALFRAME) ||
1168 (!(exstyle & WS_EX_STATICEDGE) && (style & (WS_DLGFRAME | WS_THICKFRAME))))
1169 exstyle |= WS_EX_WINDOWEDGE;
1170 else
1171 exstyle &= ~WS_EX_WINDOWEDGE;
1172 return exstyle;
1175 /* Change the owner of a window. */
1176 static HWND set_window_owner( HWND hwnd, HWND owner )
1178 WND *win = get_win_ptr( hwnd );
1179 HWND ret = 0;
1181 if (!win || win == WND_DESKTOP) return 0;
1182 if (win == WND_OTHER_PROCESS)
1184 if (is_window(hwnd)) ERR( "cannot set owner %p on other process window %p\n", owner, hwnd );
1185 return 0;
1187 SERVER_START_REQ( set_window_owner )
1189 req->handle = wine_server_user_handle( hwnd );
1190 req->owner = wine_server_user_handle( owner );
1191 if (!wine_server_call( req ))
1193 win->owner = wine_server_ptr_handle( reply->full_owner );
1194 ret = wine_server_ptr_handle( reply->prev_owner );
1197 SERVER_END_REQ;
1198 release_win_ptr( win );
1199 return ret;
1202 /* Helper function for SetWindowLong(). */
1203 LONG_PTR set_window_long( HWND hwnd, INT offset, UINT size, LONG_PTR newval, BOOL ansi )
1205 BOOL ok, made_visible = FALSE, layered = FALSE;
1206 LONG_PTR retval = 0;
1207 STYLESTRUCT style;
1208 WND *win;
1210 TRACE( "%p %d %lx %c\n", hwnd, offset, newval, ansi ? 'A' : 'W' );
1212 if (is_broadcast(hwnd))
1214 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
1215 return FALSE;
1218 if (!(win = get_win_ptr( hwnd )))
1220 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
1221 return 0;
1223 if (win == WND_DESKTOP)
1225 /* can't change anything on the desktop window */
1226 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
1227 return 0;
1229 if (win == WND_OTHER_PROCESS)
1231 if (offset == GWLP_WNDPROC)
1233 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
1234 return 0;
1236 if (offset > 32767 || offset < -32767)
1238 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1239 return 0;
1241 return send_message( hwnd, WM_WINE_SETWINDOWLONG, MAKEWPARAM( offset, size ), newval );
1244 /* first some special cases */
1245 switch( offset )
1247 case GWL_STYLE:
1248 style.styleOld = win->dwStyle;
1249 style.styleNew = newval;
1250 release_win_ptr( win );
1251 send_message( hwnd, WM_STYLECHANGING, GWL_STYLE, (LPARAM)&style );
1252 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
1253 newval = style.styleNew;
1254 /* WS_CLIPSIBLINGS can't be reset on top-level windows */
1255 if (win->parent == get_desktop_window()) newval |= WS_CLIPSIBLINGS;
1256 /* WS_MINIMIZE can't be reset */
1257 if (win->dwStyle & WS_MINIMIZE) newval |= WS_MINIMIZE;
1258 break;
1259 case GWL_EXSTYLE:
1260 style.styleOld = win->dwExStyle;
1261 style.styleNew = newval;
1262 release_win_ptr( win );
1263 send_message( hwnd, WM_STYLECHANGING, GWL_EXSTYLE, (LPARAM)&style );
1264 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
1265 /* WS_EX_TOPMOST can only be changed through SetWindowPos */
1266 newval = (style.styleNew & ~WS_EX_TOPMOST) | (win->dwExStyle & WS_EX_TOPMOST);
1267 newval = fix_exstyle(win->dwStyle, newval);
1268 break;
1269 case GWLP_HWNDPARENT:
1270 if (win->parent == get_desktop_window())
1272 release_win_ptr( win );
1273 return (ULONG_PTR)set_window_owner( hwnd, (HWND)newval );
1275 else
1277 release_win_ptr( win );
1278 return (ULONG_PTR)NtUserSetParent( hwnd, (HWND)newval );
1280 case GWLP_WNDPROC:
1282 WNDPROC proc;
1283 UINT old_flags = win->flags;
1284 retval = get_window_long_ptr( hwnd, offset, ansi );
1285 proc = alloc_winproc( (WNDPROC)newval, ansi );
1286 if (proc) win->winproc = proc;
1287 if (is_winproc_unicode( proc, !ansi )) win->flags |= WIN_ISUNICODE;
1288 else win->flags &= ~WIN_ISUNICODE;
1289 if (!((old_flags ^ win->flags) & WIN_ISUNICODE))
1291 release_win_ptr( win );
1292 return retval;
1294 /* update is_unicode flag on the server side */
1295 break;
1297 case GWLP_ID:
1298 case GWLP_HINSTANCE:
1299 case GWLP_USERDATA:
1300 break;
1301 default:
1302 if (offset < 0 || offset > (int)(win->cbWndExtra - size))
1304 WARN("Invalid offset %d\n", offset );
1305 release_win_ptr( win );
1306 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1307 return 0;
1309 else if (get_win_data( (char *)win->wExtra + offset, size ) == newval)
1311 /* already set to the same value */
1312 release_win_ptr( win );
1313 return newval;
1315 break;
1318 SERVER_START_REQ( set_window_info )
1320 req->handle = wine_server_user_handle( hwnd );
1321 req->extra_offset = -1;
1322 switch(offset)
1324 case GWL_STYLE:
1325 req->flags = SET_WIN_STYLE | SET_WIN_EXSTYLE;
1326 req->style = newval;
1327 req->ex_style = fix_exstyle(newval, win->dwExStyle);
1328 break;
1329 case GWL_EXSTYLE:
1330 req->flags = SET_WIN_EXSTYLE;
1331 req->ex_style = newval;
1332 break;
1333 case GWLP_ID:
1334 req->flags = SET_WIN_ID;
1335 req->extra_value = newval;
1336 break;
1337 case GWLP_HINSTANCE:
1338 req->flags = SET_WIN_INSTANCE;
1339 req->instance = wine_server_client_ptr( (void *)newval );
1340 break;
1341 case GWLP_WNDPROC:
1342 req->flags = SET_WIN_UNICODE;
1343 req->is_unicode = (win->flags & WIN_ISUNICODE) != 0;
1344 break;
1345 case GWLP_USERDATA:
1346 if (size == sizeof(WORD)) newval = MAKELONG( newval, win->userdata >> 16 );
1347 req->flags = SET_WIN_USERDATA;
1348 req->user_data = newval;
1349 break;
1350 default:
1351 req->flags = SET_WIN_EXTRA;
1352 req->extra_offset = offset;
1353 req->extra_size = size;
1354 set_win_data( &req->extra_value, newval, size );
1356 if ((ok = !wine_server_call_err( req )))
1358 switch(offset)
1360 case GWL_STYLE:
1361 win->dwStyle = newval;
1362 win->dwExStyle = fix_exstyle(win->dwStyle, win->dwExStyle);
1363 retval = reply->old_style;
1364 break;
1365 case GWL_EXSTYLE:
1366 win->dwExStyle = newval;
1367 retval = reply->old_ex_style;
1368 break;
1369 case GWLP_ID:
1370 win->wIDmenu = newval;
1371 retval = reply->old_id;
1372 break;
1373 case GWLP_HINSTANCE:
1374 win->hInstance = (HINSTANCE)newval;
1375 retval = (ULONG_PTR)wine_server_get_ptr( reply->old_instance );
1376 break;
1377 case GWLP_WNDPROC:
1378 break;
1379 case GWLP_USERDATA:
1380 win->userdata = newval;
1381 retval = reply->old_user_data;
1382 break;
1383 default:
1384 retval = get_win_data( (char *)win->wExtra + offset, size );
1385 set_win_data( (char *)win->wExtra + offset, newval, size );
1386 break;
1390 SERVER_END_REQ;
1392 if (offset == GWL_EXSTYLE && ((style.styleOld ^ style.styleNew) & WS_EX_LAYERED)) layered = TRUE;
1393 if ((offset == GWL_STYLE && ((style.styleOld ^ style.styleNew) & WS_VISIBLE)) || layered)
1395 made_visible = (win->dwStyle & WS_VISIBLE) != 0;
1396 invalidate_dce( win, NULL );
1398 release_win_ptr( win );
1400 if (!ok) return 0;
1402 if (offset == GWL_STYLE || offset == GWL_EXSTYLE)
1404 style.styleOld = retval;
1405 style.styleNew = newval;
1406 user_driver->pSetWindowStyle( hwnd, offset, &style );
1407 if (made_visible || layered) update_window_state( hwnd );
1408 send_message( hwnd, WM_STYLECHANGED, offset, (LPARAM)&style );
1411 return retval;
1414 /**********************************************************************
1415 * NtUserSetWindowWord (win32u.@)
1417 WORD WINAPI NtUserSetWindowWord( HWND hwnd, INT offset, WORD newval )
1419 if (offset < 0 && offset != GWLP_USERDATA)
1421 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1422 return 0;
1424 return set_window_long( hwnd, offset, sizeof(WORD), newval, TRUE );
1427 /**********************************************************************
1428 * NtUserSetWindowLong (win32u.@)
1430 LONG WINAPI NtUserSetWindowLong( HWND hwnd, INT offset, LONG newval, BOOL ansi )
1432 return set_window_long( hwnd, offset, sizeof(LONG), newval, ansi );
1435 /*****************************************************************************
1436 * NtUserSetWindowLongPtr (win32u.@)
1438 LONG_PTR WINAPI NtUserSetWindowLongPtr( HWND hwnd, INT offset, LONG_PTR newval, BOOL ansi )
1440 return set_window_long( hwnd, offset, sizeof(LONG_PTR), newval, ansi );
1443 BOOL win32u_set_window_pixel_format( HWND hwnd, int format, BOOL internal )
1445 WND *win = get_win_ptr( hwnd );
1447 if (!win || win == WND_DESKTOP || win == WND_OTHER_PROCESS)
1449 WARN( "setting format %d on win %p not supported\n", format, hwnd );
1450 return FALSE;
1452 if (internal)
1453 win->internal_pixel_format = format;
1454 else
1455 win->pixel_format = format;
1456 release_win_ptr( win );
1458 update_window_state( hwnd );
1459 return TRUE;
1462 int win32u_get_window_pixel_format( HWND hwnd )
1464 WND *win = get_win_ptr( hwnd );
1465 int ret;
1467 if (!win || win == WND_DESKTOP || win == WND_OTHER_PROCESS)
1469 WARN( "getting format on win %p not supported\n", hwnd );
1470 return 0;
1473 ret = win->pixel_format;
1474 release_win_ptr( win );
1476 return ret;
1479 /***********************************************************************
1480 * NtUserGetProp (win32u.@)
1482 * NOTE Native allows only ATOMs as the second argument. We allow strings
1483 * to save extra server call in GetPropW.
1485 HANDLE WINAPI NtUserGetProp( HWND hwnd, const WCHAR *str )
1487 ULONG_PTR ret = 0;
1489 SERVER_START_REQ( get_window_property )
1491 req->window = wine_server_user_handle( hwnd );
1492 if (IS_INTRESOURCE(str)) req->atom = LOWORD(str);
1493 else wine_server_add_data( req, str, lstrlenW(str) * sizeof(WCHAR) );
1494 if (!wine_server_call_err( req )) ret = reply->data;
1496 SERVER_END_REQ;
1497 return (HANDLE)ret;
1500 /*****************************************************************************
1501 * NtUserSetProp (win32u.@)
1503 * NOTE Native allows only ATOMs as the second argument. We allow strings
1504 * to save extra server call in SetPropW.
1506 BOOL WINAPI NtUserSetProp( HWND hwnd, const WCHAR *str, HANDLE handle )
1508 BOOL ret;
1510 SERVER_START_REQ( set_window_property )
1512 req->window = wine_server_user_handle( hwnd );
1513 req->data = (ULONG_PTR)handle;
1514 if (IS_INTRESOURCE(str)) req->atom = LOWORD(str);
1515 else wine_server_add_data( req, str, lstrlenW(str) * sizeof(WCHAR) );
1516 ret = !wine_server_call_err( req );
1518 SERVER_END_REQ;
1519 return ret;
1523 /***********************************************************************
1524 * NtUserRemoveProp (win32u.@)
1526 * NOTE Native allows only ATOMs as the second argument. We allow strings
1527 * to save extra server call in RemovePropW.
1529 HANDLE WINAPI NtUserRemoveProp( HWND hwnd, const WCHAR *str )
1531 ULONG_PTR ret = 0;
1533 SERVER_START_REQ( remove_window_property )
1535 req->window = wine_server_user_handle( hwnd );
1536 if (IS_INTRESOURCE(str)) req->atom = LOWORD(str);
1537 else wine_server_add_data( req, str, lstrlenW(str) * sizeof(WCHAR) );
1538 if (!wine_server_call_err( req )) ret = reply->data;
1540 SERVER_END_REQ;
1542 return (HANDLE)ret;
1545 static void mirror_rect( const RECT *window_rect, RECT *rect )
1547 int width = window_rect->right - window_rect->left;
1548 int tmp = rect->left;
1549 rect->left = width - rect->right;
1550 rect->right = width - tmp;
1553 /***********************************************************************
1554 * get_window_rects
1556 * Get the window and client rectangles.
1558 BOOL get_window_rects( HWND hwnd, enum coords_relative relative, RECT *window_rect,
1559 RECT *client_rect, UINT dpi )
1561 WND *win = get_win_ptr( hwnd );
1562 BOOL ret = TRUE;
1564 if (!win)
1566 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
1567 return FALSE;
1569 if (win == WND_DESKTOP)
1571 RECT rect;
1572 rect.left = rect.top = 0;
1573 if (hwnd == get_hwnd_message_parent())
1575 rect.right = 100;
1576 rect.bottom = 100;
1577 rect = map_dpi_rect( rect, get_dpi_for_window( hwnd ), dpi );
1579 else
1581 rect = get_primary_monitor_rect( dpi );
1583 if (window_rect) *window_rect = rect;
1584 if (client_rect) *client_rect = rect;
1585 return TRUE;
1587 if (win != WND_OTHER_PROCESS)
1589 UINT window_dpi = get_dpi_for_window( hwnd );
1590 RECT window = win->window_rect;
1591 RECT client = win->client_rect;
1593 switch (relative)
1595 case COORDS_CLIENT:
1596 OffsetRect( &window, -win->client_rect.left, -win->client_rect.top );
1597 OffsetRect( &client, -win->client_rect.left, -win->client_rect.top );
1598 if (win->dwExStyle & WS_EX_LAYOUTRTL)
1599 mirror_rect( &win->client_rect, &window );
1600 break;
1601 case COORDS_WINDOW:
1602 OffsetRect( &window, -win->window_rect.left, -win->window_rect.top );
1603 OffsetRect( &client, -win->window_rect.left, -win->window_rect.top );
1604 if (win->dwExStyle & WS_EX_LAYOUTRTL)
1605 mirror_rect( &win->window_rect, &client );
1606 break;
1607 case COORDS_PARENT:
1608 if (win->parent)
1610 WND *parent = get_win_ptr( win->parent );
1611 if (parent == WND_DESKTOP) break;
1612 if (!parent || parent == WND_OTHER_PROCESS)
1614 release_win_ptr( win );
1615 goto other_process;
1617 if (parent->flags & WIN_CHILDREN_MOVED)
1619 release_win_ptr( parent );
1620 release_win_ptr( win );
1621 goto other_process;
1623 if (parent->dwExStyle & WS_EX_LAYOUTRTL)
1625 mirror_rect( &parent->client_rect, &window );
1626 mirror_rect( &parent->client_rect, &client );
1628 release_win_ptr( parent );
1630 break;
1631 case COORDS_SCREEN:
1632 while (win->parent)
1634 WND *parent = get_win_ptr( win->parent );
1635 if (parent == WND_DESKTOP) break;
1636 if (!parent || parent == WND_OTHER_PROCESS)
1638 release_win_ptr( win );
1639 goto other_process;
1641 release_win_ptr( win );
1642 if (parent->flags & WIN_CHILDREN_MOVED)
1644 release_win_ptr( parent );
1645 goto other_process;
1647 win = parent;
1648 if (win->parent)
1650 OffsetRect( &window, win->client_rect.left, win->client_rect.top );
1651 OffsetRect( &client, win->client_rect.left, win->client_rect.top );
1654 break;
1656 if (window_rect) *window_rect = map_dpi_rect( window, window_dpi, dpi );
1657 if (client_rect) *client_rect = map_dpi_rect( client, window_dpi, dpi );
1658 release_win_ptr( win );
1659 return TRUE;
1662 other_process:
1663 SERVER_START_REQ( get_window_rectangles )
1665 req->handle = wine_server_user_handle( hwnd );
1666 req->relative = relative;
1667 req->dpi = dpi;
1668 if ((ret = !wine_server_call_err( req )))
1670 if (window_rect) *window_rect = wine_server_get_rect( reply->window );
1671 if (client_rect) *client_rect = wine_server_get_rect( reply->client );
1674 SERVER_END_REQ;
1675 return ret;
1678 /* see GetWindowRect */
1679 BOOL get_window_rect( HWND hwnd, RECT *rect, UINT dpi )
1681 return get_window_rects( hwnd, COORDS_SCREEN, rect, NULL, dpi );
1684 /* see GetClientRect */
1685 BOOL get_client_rect( HWND hwnd, RECT *rect, UINT dpi )
1687 return get_window_rects( hwnd, COORDS_CLIENT, NULL, rect, dpi );
1690 /* see GetWindowInfo */
1691 static BOOL get_window_info( HWND hwnd, WINDOWINFO *info )
1694 if (!info || !get_window_rects( hwnd, COORDS_SCREEN, &info->rcWindow,
1695 &info->rcClient, get_thread_dpi() ))
1696 return FALSE;
1698 info->dwStyle = get_window_long( hwnd, GWL_STYLE );
1699 info->dwExStyle = get_window_long( hwnd, GWL_EXSTYLE );
1700 info->dwWindowStatus = get_active_window() == hwnd ? WS_ACTIVECAPTION : 0;
1701 info->cxWindowBorders = info->rcClient.left - info->rcWindow.left;
1702 info->cyWindowBorders = info->rcWindow.bottom - info->rcClient.bottom;
1703 info->atomWindowType = get_class_long( hwnd, GCW_ATOM, FALSE );
1704 info->wCreatorVersion = 0x0400;
1705 return TRUE;
1708 static NTSTATUS get_window_region( HWND hwnd, BOOL surface, HRGN *region, RECT *visible )
1710 NTSTATUS status;
1711 RGNDATA *data;
1712 size_t size = 256;
1714 *region = 0;
1717 if (!(data = malloc( FIELD_OFFSET( RGNDATA, Buffer[size] )))) return STATUS_NO_MEMORY;
1719 SERVER_START_REQ( get_window_region )
1721 req->window = wine_server_user_handle( hwnd );
1722 req->surface = surface;
1723 wine_server_set_reply( req, data->Buffer, size );
1724 if (!(status = wine_server_call( req )))
1726 size_t reply_size = wine_server_reply_size( reply );
1727 if (reply_size)
1729 data->rdh.dwSize = sizeof(data->rdh);
1730 data->rdh.iType = RDH_RECTANGLES;
1731 data->rdh.nCount = reply_size / sizeof(RECT);
1732 data->rdh.nRgnSize = reply_size;
1733 *region = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1734 *visible = wine_server_get_rect( reply->visible_rect );
1737 else size = reply->total_size;
1739 SERVER_END_REQ;
1741 free( data );
1742 } while (status == STATUS_BUFFER_OVERFLOW);
1744 return status;
1747 /***********************************************************************
1748 * update_surface_region
1750 static void update_surface_region( HWND hwnd )
1752 WND *win = get_win_ptr( hwnd );
1753 HRGN region, shape = 0;
1754 RECT visible;
1756 if (!win || win == WND_DESKTOP || win == WND_OTHER_PROCESS) return;
1757 if (!win->surface) goto done;
1759 if (get_window_region( hwnd, FALSE, &shape, &visible )) goto done;
1760 if (shape)
1762 region = NtGdiCreateRectRgn( 0, 0, visible.right - visible.left, visible.bottom - visible.top );
1763 NtGdiCombineRgn( shape, shape, region, RGN_AND );
1764 if (win->dwExStyle & WS_EX_LAYOUTRTL) NtUserMirrorRgn( hwnd, shape );
1765 NtGdiDeleteObjectApp( region );
1767 window_surface_set_shape( win->surface, shape );
1769 if (get_window_region( hwnd, TRUE, &region, &visible )) goto done;
1770 if (!region) window_surface_set_clip( win->surface, shape );
1771 else
1773 NtGdiOffsetRgn( region, -visible.left, -visible.top );
1774 if (shape) NtGdiCombineRgn( region, region, shape, RGN_AND );
1775 window_surface_set_clip( win->surface, region );
1776 NtGdiDeleteObjectApp( region );
1779 done:
1780 if (shape) NtGdiDeleteObjectApp( shape );
1781 release_win_ptr( win );
1785 static BOOL get_surface_rect( const RECT *visible_rect, RECT *surface_rect )
1787 RECT virtual_rect = NtUserGetVirtualScreenRect();
1789 *surface_rect = *visible_rect;
1791 /* crop surfaces which are larger than the virtual screen rect, some applications create huge windows */
1792 if ((surface_rect->right - surface_rect->left > virtual_rect.right - virtual_rect.left ||
1793 surface_rect->bottom - surface_rect->top > virtual_rect.bottom - virtual_rect.top) &&
1794 !intersect_rect( surface_rect, surface_rect, &virtual_rect ))
1795 return FALSE;
1796 OffsetRect( surface_rect, -visible_rect->left, -visible_rect->top );
1798 /* round the surface coordinates to avoid re-creating them too often on resize */
1799 surface_rect->left &= ~127;
1800 surface_rect->top &= ~127;
1801 surface_rect->right = max( surface_rect->left + 128, (surface_rect->right + 127) & ~127 );
1802 surface_rect->bottom = max( surface_rect->top + 128, (surface_rect->bottom + 127) & ~127 );
1803 return TRUE;
1806 static BOOL get_default_window_surface( HWND hwnd, const RECT *surface_rect, struct window_surface **surface )
1808 struct window_surface *previous;
1809 WND *win;
1811 TRACE( "hwnd %p, surface_rect %s, surface %p\n", hwnd, wine_dbgstr_rect( surface_rect ), surface );
1813 if (!(win = get_win_ptr( hwnd )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS) return FALSE;
1815 if ((previous = win->surface) && EqualRect( &previous->rect, surface_rect ))
1817 window_surface_add_ref( (*surface = previous) );
1818 TRACE( "trying to reuse previous surface %p\n", previous );
1820 else if (!win->parent || win->parent == get_desktop_window())
1822 *surface = &dummy_surface; /* provide a default surface for top-level windows */
1823 window_surface_add_ref( *surface );
1825 else
1827 *surface = NULL; /* use parent surface for child windows */
1828 TRACE( "using parent window surface\n" );
1831 release_win_ptr( win );
1832 return TRUE;
1835 static struct window_surface *create_window_surface( HWND hwnd, UINT swp_flags, BOOL create_layered,
1836 const RECT *window_rect, const RECT *client_rect,
1837 RECT *visible_rect, RECT *surface_rect )
1839 BOOL shaped, needs_surface, create_opaque, is_layered;
1840 struct window_surface *new_surface;
1841 RECT dummy;
1842 HRGN shape;
1844 if (get_window_region( hwnd, FALSE, &shape, &dummy )) shaped = FALSE;
1845 else if ((shaped = !!shape)) NtGdiDeleteObjectApp( shape );
1847 *visible_rect = *window_rect;
1848 if (!user_driver->pWindowPosChanging( hwnd, swp_flags, shaped, window_rect, client_rect, visible_rect )) needs_surface = FALSE;
1849 else if (swp_flags & SWP_HIDEWINDOW) needs_surface = FALSE;
1850 else if (swp_flags & SWP_SHOWWINDOW) needs_surface = TRUE;
1851 else needs_surface = !!(NtUserGetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE);
1853 if (!get_surface_rect( visible_rect, surface_rect )) needs_surface = FALSE;
1854 if (!get_default_window_surface( hwnd, surface_rect, &new_surface )) return NULL;
1856 is_layered = new_surface && new_surface->alpha_mask;
1857 create_opaque = !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED);
1859 if ((create_opaque && is_layered) || (create_layered && !is_layered))
1861 window_surface_release( new_surface );
1862 new_surface = &dummy_surface;
1863 window_surface_add_ref( new_surface );
1866 if (!needs_surface || IsRectEmpty( visible_rect )) needs_surface = FALSE; /* use default surface */
1867 else needs_surface = !user_driver->pCreateWindowSurface( hwnd, surface_rect, &new_surface );
1869 /* create or update window surface for top-level windows if the driver doesn't implement CreateWindowSurface */
1870 if (needs_surface && new_surface == &dummy_surface && (create_opaque && !create_layered))
1872 window_surface_release( new_surface );
1873 create_offscreen_window_surface( hwnd, surface_rect, &new_surface );
1876 if (new_surface && !is_layered)
1878 DWORD lwa_flags = 0;
1879 COLORREF key;
1880 BYTE alpha;
1882 if (!NtUserGetLayeredWindowAttributes( hwnd, &key, &alpha, &lwa_flags )) lwa_flags = 0;
1883 if (!(lwa_flags & LWA_ALPHA)) alpha = 255;
1884 if (!(lwa_flags & LWA_COLORKEY)) key = CLR_INVALID;
1885 window_surface_set_layered( new_surface, key, alpha << 24, 0 );
1888 return new_surface;
1891 /***********************************************************************
1892 * apply_window_pos
1894 * Backend implementation of SetWindowPos.
1896 static BOOL apply_window_pos( HWND hwnd, HWND insert_after, UINT swp_flags, struct window_surface *new_surface,
1897 const RECT *window_rect, const RECT *client_rect, const RECT *visible_rect, const RECT *valid_rects )
1899 WND *win;
1900 HWND surface_win = 0;
1901 BOOL ret, needs_update = FALSE;
1902 RECT old_visible_rect, old_window_rect, old_client_rect, extra_rects[3];
1903 struct window_surface *old_surface;
1905 get_window_rects( hwnd, COORDS_SCREEN, &old_window_rect, NULL, get_thread_dpi() );
1906 if (IsRectEmpty( &valid_rects[0] )) valid_rects = NULL;
1908 if (!(win = get_win_ptr( hwnd )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS) return FALSE;
1910 old_visible_rect = win->visible_rect;
1911 old_client_rect = win->client_rect;
1912 old_surface = win->surface;
1913 if (old_surface != new_surface) swp_flags |= SWP_FRAMECHANGED; /* force refreshing non-client area */
1914 if (new_surface == &dummy_surface) swp_flags |= SWP_NOREDRAW;
1915 else if (old_surface == &dummy_surface)
1917 swp_flags |= SWP_NOCOPYBITS;
1918 valid_rects = NULL;
1921 SERVER_START_REQ( set_window_pos )
1923 req->handle = wine_server_user_handle( hwnd );
1924 req->previous = wine_server_user_handle( insert_after );
1925 req->swp_flags = swp_flags;
1926 req->window = wine_server_rectangle( *window_rect );
1927 req->client = wine_server_rectangle( *client_rect );
1928 if (!EqualRect( window_rect, visible_rect ) || new_surface || valid_rects)
1930 extra_rects[0] = extra_rects[1] = *visible_rect;
1931 if (new_surface)
1933 extra_rects[1] = new_surface->rect;
1934 OffsetRect( &extra_rects[1], visible_rect->left, visible_rect->top );
1936 if (valid_rects) extra_rects[2] = valid_rects[0];
1937 else SetRectEmpty( &extra_rects[2] );
1938 wine_server_add_data( req, extra_rects, sizeof(extra_rects) );
1940 if (new_surface) req->paint_flags |= SET_WINPOS_PAINT_SURFACE;
1941 if (win->pixel_format || win->internal_pixel_format)
1942 req->paint_flags |= SET_WINPOS_PIXEL_FORMAT;
1944 if ((ret = !wine_server_call( req )))
1946 win->dwStyle = reply->new_style;
1947 win->dwExStyle = reply->new_ex_style;
1948 win->window_rect = *window_rect;
1949 win->client_rect = *client_rect;
1950 win->visible_rect = *visible_rect;
1951 if ((win->surface = new_surface)) window_surface_add_ref( win->surface );
1952 surface_win = wine_server_ptr_handle( reply->surface_win );
1953 needs_update = reply->needs_update;
1954 if (get_window_long( win->parent, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
1956 RECT client;
1957 get_window_rects( win->parent, COORDS_CLIENT, NULL, &client, get_thread_dpi() );
1958 mirror_rect( &client, &win->window_rect );
1959 mirror_rect( &client, &win->client_rect );
1960 mirror_rect( &client, &win->visible_rect );
1962 /* if an RTL window is resized the children have moved */
1963 if (win->dwExStyle & WS_EX_LAYOUTRTL &&
1964 client_rect->right - client_rect->left != old_client_rect.right - old_client_rect.left)
1965 win->flags |= WIN_CHILDREN_MOVED;
1968 SERVER_END_REQ;
1970 if (ret)
1972 if (needs_update) update_surface_region( surface_win );
1973 if (((swp_flags & SWP_AGG_NOPOSCHANGE) != SWP_AGG_NOPOSCHANGE) ||
1974 (swp_flags & (SWP_HIDEWINDOW | SWP_SHOWWINDOW | SWP_STATECHANGED | SWP_FRAMECHANGED)))
1975 invalidate_dce( win, &old_window_rect );
1978 release_win_ptr( win );
1980 if (ret)
1982 TRACE( "win %p surface %p -> %p\n", hwnd, old_surface, new_surface );
1983 register_window_surface( old_surface, new_surface );
1984 if (old_surface)
1986 if (valid_rects)
1988 if (old_surface != new_surface)
1989 move_window_bits_surface( hwnd, window_rect, old_surface, &old_visible_rect, valid_rects );
1990 else
1991 move_window_bits( hwnd, visible_rect, &old_visible_rect, window_rect, valid_rects );
1992 valid_rects = NULL; /* prevent the driver from trying to also move the bits */
1994 window_surface_release( old_surface );
1996 else if (surface_win && surface_win != hwnd)
1998 if (valid_rects)
2000 RECT rects[2];
2001 int x_offset = old_visible_rect.left - visible_rect->left;
2002 int y_offset = old_visible_rect.top - visible_rect->top;
2004 /* if all that happened is that the whole window moved, copy everything */
2005 if (!(swp_flags & SWP_FRAMECHANGED) &&
2006 old_visible_rect.right - visible_rect->right == x_offset &&
2007 old_visible_rect.bottom - visible_rect->bottom == y_offset &&
2008 old_client_rect.left - client_rect->left == x_offset &&
2009 old_client_rect.right - client_rect->right == x_offset &&
2010 old_client_rect.top - client_rect->top == y_offset &&
2011 old_client_rect.bottom - client_rect->bottom == y_offset &&
2012 EqualRect( &valid_rects[0], client_rect ))
2014 rects[0] = *visible_rect;
2015 rects[1] = old_visible_rect;
2016 valid_rects = rects;
2018 move_window_bits( hwnd, visible_rect, visible_rect, window_rect, valid_rects );
2019 valid_rects = NULL; /* prevent the driver from trying to also move the bits */
2023 user_driver->pWindowPosChanged( hwnd, insert_after, swp_flags, window_rect,
2024 client_rect, visible_rect, valid_rects, new_surface );
2027 return ret;
2030 /*******************************************************************
2031 * NtUserGetWindowRgnEx (win32u.@)
2033 int WINAPI NtUserGetWindowRgnEx( HWND hwnd, HRGN hrgn, UINT unk )
2035 NTSTATUS status;
2036 int ret = ERROR;
2037 HRGN win_rgn;
2038 RECT visible;
2040 if ((status = get_window_region( hwnd, FALSE, &win_rgn, &visible )))
2042 set_ntstatus( status );
2043 return ERROR;
2046 if (win_rgn)
2048 ret = NtGdiCombineRgn( hrgn, win_rgn, 0, RGN_COPY );
2049 NtGdiDeleteObjectApp( win_rgn );
2051 return ret;
2054 /***********************************************************************
2055 * NtUserSetWindowRgn (win32u.@)
2057 int WINAPI NtUserSetWindowRgn( HWND hwnd, HRGN hrgn, BOOL redraw )
2059 static const RECT empty_rect;
2060 BOOL ret;
2062 if (hrgn)
2064 RGNDATA *data;
2065 DWORD size;
2067 if (!(size = NtGdiGetRegionData( hrgn, 0, NULL ))) return FALSE;
2068 if (!(data = malloc( size ))) return FALSE;
2069 if (!NtGdiGetRegionData( hrgn, size, data ))
2071 free( data );
2072 return FALSE;
2074 SERVER_START_REQ( set_window_region )
2076 req->window = wine_server_user_handle( hwnd );
2077 req->redraw = redraw != 0;
2078 if (data->rdh.nCount)
2079 wine_server_add_data( req, data->Buffer, data->rdh.nCount * sizeof(RECT) );
2080 else
2081 wine_server_add_data( req, &empty_rect, sizeof(empty_rect) );
2082 ret = !wine_server_call_err( req );
2084 SERVER_END_REQ;
2085 free( data );
2087 else /* clear existing region */
2089 SERVER_START_REQ( set_window_region )
2091 req->window = wine_server_user_handle( hwnd );
2092 req->redraw = redraw != 0;
2093 ret = !wine_server_call_err( req );
2095 SERVER_END_REQ;
2098 if (ret)
2100 UINT swp_flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED |
2101 SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE;
2102 if (!redraw) swp_flags |= SWP_NOREDRAW;
2103 user_driver->pSetWindowRgn( hwnd, hrgn, redraw );
2104 NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, swp_flags );
2105 if (hrgn) NtGdiDeleteObjectApp( hrgn );
2107 return ret;
2110 /***********************************************************************
2111 * NtUserMoveWindow (win32u.@)
2113 BOOL WINAPI NtUserMoveWindow( HWND hwnd, INT x, INT y, INT cx, INT cy, BOOL repaint )
2115 int flags = SWP_NOZORDER | SWP_NOACTIVATE;
2116 if (!repaint) flags |= SWP_NOREDRAW;
2117 TRACE( "%p %d,%d %dx%d %d\n", hwnd, x, y, cx, cy, repaint );
2118 return NtUserSetWindowPos( hwnd, 0, x, y, cx, cy, flags );
2121 /*****************************************************************************
2122 * NtUserGetLayeredWindowAttributes (win32u.@)
2124 BOOL WINAPI NtUserGetLayeredWindowAttributes( HWND hwnd, COLORREF *key, BYTE *alpha, DWORD *flags )
2126 BOOL ret;
2128 SERVER_START_REQ( get_window_layered_info )
2130 req->handle = wine_server_user_handle( hwnd );
2131 if ((ret = !wine_server_call_err( req )))
2133 if (key) *key = reply->color_key;
2134 if (alpha) *alpha = reply->alpha;
2135 if (flags) *flags = reply->flags;
2138 SERVER_END_REQ;
2140 return ret;
2143 /*****************************************************************************
2144 * NtUserSetLayeredWindowAttributes (win32u.@)
2146 BOOL WINAPI NtUserSetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha, DWORD flags )
2148 BOOL ret;
2150 TRACE( "(%p,%s,%d,%x)\n", hwnd, debugstr_color(key), alpha, (int)flags );
2152 SERVER_START_REQ( set_window_layered_info )
2154 req->handle = wine_server_user_handle( hwnd );
2155 req->color_key = key;
2156 req->alpha = alpha;
2157 req->flags = flags;
2158 ret = !wine_server_call_err( req );
2160 SERVER_END_REQ;
2162 if (ret)
2164 user_driver->pSetLayeredWindowAttributes( hwnd, key, alpha, flags );
2165 update_window_state( hwnd );
2168 return ret;
2171 /*****************************************************************************
2172 * UpdateLayeredWindow (win32u.@)
2174 BOOL WINAPI NtUserUpdateLayeredWindow( HWND hwnd, HDC hdc_dst, const POINT *pts_dst, const SIZE *size,
2175 HDC hdc_src, const POINT *pts_src, COLORREF key,
2176 const BLENDFUNCTION *blend, DWORD flags, const RECT *dirty )
2178 DWORD swp_flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW;
2179 struct window_surface *surface;
2180 RECT window_rect, client_rect, visible_rect, surface_rect;
2181 SIZE offset;
2182 BOOL ret = FALSE;
2184 if (flags & ~(ULW_COLORKEY | ULW_ALPHA | ULW_OPAQUE | ULW_EX_NORESIZE) ||
2185 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED) ||
2186 NtUserGetLayeredWindowAttributes( hwnd, NULL, NULL, NULL ))
2188 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
2189 return FALSE;
2192 get_window_rects( hwnd, COORDS_PARENT, &window_rect, &client_rect, get_thread_dpi() );
2194 if (pts_dst)
2196 offset.cx = pts_dst->x - window_rect.left;
2197 offset.cy = pts_dst->y - window_rect.top;
2198 OffsetRect( &client_rect, offset.cx, offset.cy );
2199 OffsetRect( &window_rect, offset.cx, offset.cy );
2200 swp_flags &= ~SWP_NOMOVE;
2202 if (size)
2204 offset.cx = size->cx - (window_rect.right - window_rect.left);
2205 offset.cy = size->cy - (window_rect.bottom - window_rect.top);
2206 if (size->cx <= 0 || size->cy <= 0)
2208 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
2209 return FALSE;
2211 if ((flags & ULW_EX_NORESIZE) && (offset.cx || offset.cy))
2213 RtlSetLastWin32Error( ERROR_INCORRECT_SIZE );
2214 return FALSE;
2216 client_rect.right += offset.cx;
2217 client_rect.bottom += offset.cy;
2218 window_rect.right += offset.cx;
2219 window_rect.bottom += offset.cy;
2220 swp_flags &= ~SWP_NOSIZE;
2223 TRACE( "window %p win %s client %s\n", hwnd,
2224 wine_dbgstr_rect(&window_rect), wine_dbgstr_rect(&client_rect) );
2226 surface = create_window_surface( hwnd, swp_flags, TRUE, &window_rect, &client_rect, &visible_rect, &surface_rect );
2227 apply_window_pos( hwnd, 0, swp_flags, surface, &window_rect, &client_rect, &visible_rect, NULL );
2228 if (surface) window_surface_release( surface );
2230 if (!(flags & ULW_COLORKEY)) key = CLR_INVALID;
2231 if (IsRectEmpty( &surface_rect )) window_surface_add_ref( (surface = &dummy_surface) );
2232 else if (!(user_driver->pCreateLayeredWindow( hwnd, &surface_rect, key, &surface )) || !surface) return FALSE;
2234 if (!hdc_src || surface == &dummy_surface) ret = TRUE;
2235 else
2237 BLENDFUNCTION src_blend = { AC_SRC_OVER, 0, 255, 0 };
2238 RECT rect = window_rect, src_rect;
2239 UINT alpha = 0xff;
2240 HDC hdc = NULL;
2242 OffsetRect( &rect, -rect.left, -rect.top );
2243 intersect_rect( &rect, &rect, &surface_rect );
2245 if (!(hdc = NtGdiCreateCompatibleDC( 0 ))) goto done;
2246 window_surface_lock( surface );
2247 NtGdiSelectBitmap( hdc, surface->color_bitmap );
2249 if (dirty) intersect_rect( &rect, &rect, dirty );
2250 NtGdiPatBlt( hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, BLACKNESS );
2252 src_rect = rect;
2253 if (pts_src) OffsetRect( &src_rect, pts_src->x, pts_src->y );
2254 NtGdiTransformPoints( hdc_src, (POINT *)&src_rect, (POINT *)&src_rect, 2, NtGdiDPtoLP );
2256 if (flags & ULW_ALPHA) src_blend = *blend;
2257 ret = NtGdiAlphaBlend( hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
2258 hdc_src, src_rect.left, src_rect.top, src_rect.right - src_rect.left, src_rect.bottom - src_rect.top,
2259 *(DWORD *)&src_blend, 0 );
2260 if (ret) add_bounds_rect( &surface->bounds, &rect );
2262 NtGdiDeleteObjectApp( hdc );
2263 window_surface_unlock( surface );
2265 window_surface_set_layered( surface, key, -1, 0xff000000 );
2266 window_surface_flush( surface );
2268 user_driver->pUpdateLayeredWindow( hwnd, &window_rect, key, alpha, flags );
2271 done:
2272 window_surface_release( surface );
2273 return ret;
2276 /***********************************************************************
2277 * list_children_from_point
2279 * Get the list of children that can contain point from the server.
2280 * Point is in screen coordinates.
2281 * Returned list must be freed by caller.
2283 static HWND *list_children_from_point( HWND hwnd, POINT pt )
2285 int i, size = 128;
2286 HWND *list;
2288 for (;;)
2290 int count = 0;
2292 if (!(list = malloc( size * sizeof(HWND) ))) break;
2294 SERVER_START_REQ( get_window_children_from_point )
2296 req->parent = wine_server_user_handle( hwnd );
2297 req->x = pt.x;
2298 req->y = pt.y;
2299 req->dpi = get_thread_dpi();
2300 wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
2301 if (!wine_server_call( req )) count = reply->count;
2303 SERVER_END_REQ;
2304 if (count && count < size)
2306 /* start from the end since HWND is potentially larger than user_handle_t */
2307 for (i = count - 1; i >= 0; i--)
2308 list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
2309 list[count] = 0;
2310 return list;
2312 free( list );
2313 if (!count) break;
2314 size = count + 1; /* restart with a large enough buffer */
2316 return NULL;
2319 /***********************************************************************
2320 * window_from_point
2322 * Find the window and hittest for a given point.
2324 HWND window_from_point( HWND hwnd, POINT pt, INT *hittest )
2326 int i, res;
2327 HWND ret, *list;
2328 POINT win_pt;
2329 int dpi;
2331 if (!hwnd) hwnd = get_desktop_window();
2332 if (!(dpi = get_thread_dpi())) dpi = get_win_monitor_dpi( hwnd );
2334 *hittest = HTNOWHERE;
2336 if (!(list = list_children_from_point( hwnd, pt ))) return 0;
2338 /* now determine the hittest */
2340 for (i = 0; list[i]; i++)
2342 LONG style = get_window_long( list[i], GWL_STYLE );
2344 /* If window is minimized or disabled, return at once */
2345 if (style & WS_DISABLED)
2347 *hittest = HTERROR;
2348 break;
2350 /* Send WM_NCCHITTEST (if same thread) */
2351 if (!is_current_thread_window( list[i] ))
2353 *hittest = HTCLIENT;
2354 break;
2356 win_pt = map_dpi_point( pt, dpi, get_dpi_for_window( list[i] ));
2357 res = send_message( list[i], WM_NCHITTEST, 0, MAKELPARAM( win_pt.x, win_pt.y ));
2358 if (res != HTTRANSPARENT)
2360 *hittest = res; /* Found the window */
2361 break;
2363 /* continue search with next window in z-order */
2365 ret = list[i];
2366 free( list );
2367 TRACE( "scope %p (%d,%d) returning %p\n", hwnd, (int)pt.x, (int)pt.y, ret );
2368 return ret;
2371 /*******************************************************************
2372 * NtUserWindowFromPoint (win32u.@)
2374 HWND WINAPI NtUserWindowFromPoint( LONG x, LONG y )
2376 POINT pt = { .x = x, .y = y };
2377 INT hittest;
2378 return window_from_point( 0, pt, &hittest );
2381 /*******************************************************************
2382 * NtUserChildWindowFromPointEx (win32u.@)
2384 HWND WINAPI NtUserChildWindowFromPointEx( HWND parent, LONG x, LONG y, UINT flags )
2386 POINT pt = { .x = x, .y = y }; /* in the client coordinates */
2387 HWND *list;
2388 int i;
2389 RECT rect;
2390 HWND ret;
2392 get_client_rect( parent, &rect, get_thread_dpi() );
2393 if (!PtInRect( &rect, pt )) return 0;
2394 if (!(list = list_window_children( 0, parent, NULL, 0 ))) return parent;
2396 for (i = 0; list[i]; i++)
2398 if (!get_window_rects( list[i], COORDS_PARENT, &rect, NULL, get_thread_dpi() )) continue;
2399 if (!PtInRect( &rect, pt )) continue;
2400 if (flags & (CWP_SKIPINVISIBLE|CWP_SKIPDISABLED))
2402 LONG style = get_window_long( list[i], GWL_STYLE );
2403 if ((flags & CWP_SKIPINVISIBLE) && !(style & WS_VISIBLE)) continue;
2404 if ((flags & CWP_SKIPDISABLED) && (style & WS_DISABLED)) continue;
2406 if (flags & CWP_SKIPTRANSPARENT)
2408 if (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TRANSPARENT) continue;
2410 break;
2412 ret = list[i];
2413 free( list );
2414 if (!ret) ret = parent;
2415 return ret;
2418 /*******************************************************************
2419 * NtUserRealChildWindowFromPoint (win32u.@)
2421 HWND WINAPI NtUserRealChildWindowFromPoint( HWND parent, LONG x, LONG y )
2423 return NtUserChildWindowFromPointEx( parent, x, y, CWP_SKIPTRANSPARENT | CWP_SKIPINVISIBLE );
2426 /*******************************************************************
2427 * get_work_rect
2429 * Get the work area that a maximized window can cover, depending on style.
2431 static BOOL get_work_rect( HWND hwnd, RECT *rect )
2433 HMONITOR monitor = monitor_from_window( hwnd, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
2434 MONITORINFO mon_info;
2435 DWORD style;
2437 if (!monitor) return FALSE;
2439 mon_info.cbSize = sizeof(mon_info);
2440 get_monitor_info( monitor, &mon_info, get_thread_dpi() );
2441 *rect = mon_info.rcMonitor;
2443 style = get_window_long( hwnd, GWL_STYLE );
2444 if (style & WS_MAXIMIZEBOX)
2446 if ((style & WS_CAPTION) == WS_CAPTION || !(style & (WS_CHILD | WS_POPUP)))
2447 *rect = mon_info.rcWork;
2449 return TRUE;
2452 static RECT get_maximized_work_rect( HWND hwnd )
2454 RECT work_rect = { 0 };
2456 if ((get_window_long( hwnd, GWL_STYLE ) & (WS_MINIMIZE | WS_MAXIMIZE)) == WS_MAXIMIZE)
2458 if (!get_work_rect( hwnd, &work_rect ))
2459 work_rect = get_primary_monitor_rect( get_thread_dpi() );
2461 return work_rect;
2464 /*******************************************************************
2465 * update_maximized_pos
2467 * For top level windows covering the work area, we might have to
2468 * "forget" the maximized position. Windows presumably does this
2469 * to avoid situations where the border style changes, which would
2470 * lead the window to be outside the screen, or the window gets
2471 * reloaded on a different screen, and the "saved" position no
2472 * longer applies to it (despite being maximized).
2474 * Some applications (e.g. Imperiums: Greek Wars) depend on this.
2476 static void update_maximized_pos( WND *wnd, RECT *work_rect )
2478 if (wnd->parent && wnd->parent != get_desktop_window())
2479 return;
2481 if (wnd->dwStyle & WS_MAXIMIZE)
2483 if (wnd->window_rect.left <= work_rect->left && wnd->window_rect.top <= work_rect->top &&
2484 wnd->window_rect.right >= work_rect->right && wnd->window_rect.bottom >= work_rect->bottom)
2485 wnd->max_pos.x = wnd->max_pos.y = -1;
2487 else
2488 wnd->max_pos.x = wnd->max_pos.y = -1;
2491 static BOOL empty_point( POINT pt )
2493 return pt.x == -1 && pt.y == -1;
2496 /***********************************************************************
2497 * NtUserGetWindowPlacement (win32u.@)
2499 BOOL WINAPI NtUserGetWindowPlacement( HWND hwnd, WINDOWPLACEMENT *placement )
2501 RECT work_rect = get_maximized_work_rect( hwnd );
2502 WND *win = get_win_ptr( hwnd );
2503 UINT win_dpi;
2505 if (!win) return FALSE;
2507 if (win == WND_DESKTOP)
2509 placement->length = sizeof(*placement);
2510 placement->showCmd = SW_SHOWNORMAL;
2511 placement->flags = 0;
2512 placement->ptMinPosition.x = -1;
2513 placement->ptMinPosition.y = -1;
2514 placement->ptMaxPosition.x = -1;
2515 placement->ptMaxPosition.y = -1;
2516 get_window_rect( hwnd, &placement->rcNormalPosition, get_thread_dpi() );
2517 return TRUE;
2519 if (win == WND_OTHER_PROCESS)
2521 RECT normal_position;
2522 DWORD style;
2524 if (!get_window_rect( hwnd, &normal_position, get_thread_dpi() ))
2525 return FALSE;
2527 FIXME("not fully supported on other process window %p.\n", hwnd);
2529 placement->length = sizeof(*placement);
2530 style = get_window_long( hwnd, GWL_STYLE );
2531 if (style & WS_MINIMIZE)
2532 placement->showCmd = SW_SHOWMINIMIZED;
2533 else
2534 placement->showCmd = (style & WS_MAXIMIZE) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL;
2535 /* provide some dummy information */
2536 placement->flags = 0;
2537 placement->ptMinPosition.x = -1;
2538 placement->ptMinPosition.y = -1;
2539 placement->ptMaxPosition.x = -1;
2540 placement->ptMaxPosition.y = -1;
2541 placement->rcNormalPosition = normal_position;
2542 return TRUE;
2545 /* update the placement according to the current style */
2546 if (win->dwStyle & WS_MINIMIZE)
2548 win->min_pos.x = win->window_rect.left;
2549 win->min_pos.y = win->window_rect.top;
2551 else if (win->dwStyle & WS_MAXIMIZE)
2553 win->max_pos.x = win->window_rect.left;
2554 win->max_pos.y = win->window_rect.top;
2556 else
2558 win->normal_rect = win->window_rect;
2560 update_maximized_pos( win, &work_rect );
2562 placement->length = sizeof(*placement);
2563 if (win->dwStyle & WS_MINIMIZE)
2564 placement->showCmd = SW_SHOWMINIMIZED;
2565 else
2566 placement->showCmd = ( win->dwStyle & WS_MAXIMIZE ) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL ;
2567 if (win->flags & WIN_RESTORE_MAX)
2568 placement->flags = WPF_RESTORETOMAXIMIZED;
2569 else
2570 placement->flags = 0;
2571 win_dpi = get_dpi_for_window( hwnd );
2572 placement->ptMinPosition = empty_point(win->min_pos) ? win->min_pos
2573 : map_dpi_point( win->min_pos, win_dpi, get_thread_dpi() );
2574 placement->ptMaxPosition = empty_point(win->max_pos) ? win->max_pos
2575 : map_dpi_point( win->max_pos, win_dpi, get_thread_dpi() );
2576 placement->rcNormalPosition = map_dpi_rect( win->normal_rect, win_dpi, get_thread_dpi() );
2577 release_win_ptr( win );
2579 TRACE( "%p: returning min %d,%d max %d,%d normal %s\n",
2580 hwnd, (int)placement->ptMinPosition.x, (int)placement->ptMinPosition.y,
2581 (int)placement->ptMaxPosition.x, (int)placement->ptMaxPosition.y,
2582 wine_dbgstr_rect(&placement->rcNormalPosition) );
2583 return TRUE;
2586 /* make sure the specified rect is visible on screen */
2587 static void make_rect_onscreen( RECT *rect )
2589 MONITORINFO info;
2590 HMONITOR monitor = monitor_from_rect( rect, MONITOR_DEFAULTTONEAREST, get_thread_dpi() );
2592 info.cbSize = sizeof(info);
2593 if (!monitor || !get_monitor_info( monitor, &info, get_thread_dpi() )) return;
2594 /* FIXME: map coordinates from rcWork to rcMonitor */
2595 if (rect->right <= info.rcWork.left)
2597 rect->right += info.rcWork.left - rect->left;
2598 rect->left = info.rcWork.left;
2600 else if (rect->left >= info.rcWork.right)
2602 rect->left += info.rcWork.right - rect->right;
2603 rect->right = info.rcWork.right;
2605 if (rect->bottom <= info.rcWork.top)
2607 rect->bottom += info.rcWork.top - rect->top;
2608 rect->top = info.rcWork.top;
2610 else if (rect->top >= info.rcWork.bottom)
2612 rect->top += info.rcWork.bottom - rect->bottom;
2613 rect->bottom = info.rcWork.bottom;
2617 /***********************************************************************
2618 * NtUserGetInternalWindowPos (win32u.@)
2620 UINT WINAPI NtUserGetInternalWindowPos( HWND hwnd, RECT *rect, POINT *pt )
2622 WINDOWPLACEMENT placement;
2624 placement.length = sizeof(placement);
2625 if (!NtUserGetWindowPlacement( hwnd, &placement )) return 0;
2626 if (rect) *rect = placement.rcNormalPosition;
2627 if (pt) *pt = placement.ptMinPosition;
2628 return placement.showCmd;
2631 /* make sure the specified point is visible on screen */
2632 static void make_point_onscreen( POINT *pt )
2634 RECT rect;
2636 SetRect( &rect, pt->x, pt->y, pt->x + 1, pt->y + 1 );
2637 make_rect_onscreen( &rect );
2638 pt->x = rect.left;
2639 pt->y = rect.top;
2642 static BOOL set_window_placement( HWND hwnd, const WINDOWPLACEMENT *wndpl, UINT flags )
2644 RECT work_rect = get_maximized_work_rect( hwnd );
2645 WND *win = get_win_ptr( hwnd );
2646 WINDOWPLACEMENT wp = *wndpl;
2647 DWORD style;
2649 if (flags & PLACE_MIN) make_point_onscreen( &wp.ptMinPosition );
2650 if (flags & PLACE_MAX) make_point_onscreen( &wp.ptMaxPosition );
2651 if (flags & PLACE_RECT) make_rect_onscreen( &wp.rcNormalPosition );
2653 TRACE( "%p: setting min %d,%d max %d,%d normal %s flags %x adjusted to min %d,%d max %d,%d normal %s\n",
2654 hwnd, (int)wndpl->ptMinPosition.x, (int)wndpl->ptMinPosition.y,
2655 (int)wndpl->ptMaxPosition.x, (int)wndpl->ptMaxPosition.y,
2656 wine_dbgstr_rect(&wndpl->rcNormalPosition), flags,
2657 (int)wp.ptMinPosition.x, (int)wp.ptMinPosition.y,
2658 (int)wp.ptMaxPosition.x, (int)wp.ptMaxPosition.y,
2659 wine_dbgstr_rect(&wp.rcNormalPosition) );
2661 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
2663 if (flags & PLACE_MIN) win->min_pos = point_thread_to_win_dpi( hwnd, wp.ptMinPosition );
2664 if (flags & PLACE_MAX)
2666 win->max_pos = point_thread_to_win_dpi( hwnd, wp.ptMaxPosition );
2667 update_maximized_pos( win, &work_rect );
2669 if (flags & PLACE_RECT) win->normal_rect = rect_thread_to_win_dpi( hwnd, wp.rcNormalPosition );
2671 style = win->dwStyle;
2673 release_win_ptr( win );
2675 if (style & WS_MINIMIZE)
2677 if (flags & PLACE_MIN)
2679 NtUserSetWindowPos( hwnd, 0, wp.ptMinPosition.x, wp.ptMinPosition.y, 0, 0,
2680 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
2683 else if (style & WS_MAXIMIZE)
2685 if (flags & PLACE_MAX)
2686 NtUserSetWindowPos( hwnd, 0, wp.ptMaxPosition.x, wp.ptMaxPosition.y, 0, 0,
2687 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
2689 else if (flags & PLACE_RECT)
2690 NtUserSetWindowPos( hwnd, 0, wp.rcNormalPosition.left, wp.rcNormalPosition.top,
2691 wp.rcNormalPosition.right - wp.rcNormalPosition.left,
2692 wp.rcNormalPosition.bottom - wp.rcNormalPosition.top,
2693 SWP_NOZORDER | SWP_NOACTIVATE );
2695 NtUserShowWindow( hwnd, wndpl->showCmd );
2697 if (is_iconic( hwnd ))
2699 if (wndpl->flags & WPF_RESTORETOMAXIMIZED)
2700 win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
2702 return TRUE;
2705 /***********************************************************************
2706 * NtUserSetWindowPlacement (win32u.@)
2708 BOOL WINAPI NtUserSetWindowPlacement( HWND hwnd, const WINDOWPLACEMENT *wpl )
2710 UINT flags = PLACE_MAX | PLACE_RECT;
2711 if (!wpl) return FALSE;
2712 if (wpl->length != sizeof(*wpl))
2714 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
2715 return FALSE;
2717 if (wpl->flags & WPF_SETMINPOSITION) flags |= PLACE_MIN;
2718 return set_window_placement( hwnd, wpl, flags );
2721 /*****************************************************************************
2722 * NtUserBuildHwndList (win32u.@)
2724 NTSTATUS WINAPI NtUserBuildHwndList( HDESK desktop, ULONG unk2, ULONG unk3, ULONG unk4,
2725 ULONG thread_id, ULONG count, HWND *buffer, ULONG *size )
2727 user_handle_t *list = (user_handle_t *)buffer;
2728 int i;
2729 NTSTATUS status;
2731 SERVER_START_REQ( get_window_children )
2733 req->desktop = wine_server_obj_handle( desktop );
2734 req->tid = thread_id;
2735 if (count) wine_server_set_reply( req, list, (count - 1) * sizeof(user_handle_t) );
2736 status = wine_server_call( req );
2737 if (status && status != STATUS_BUFFER_TOO_SMALL) return status;
2738 *size = reply->count + 1;
2740 SERVER_END_REQ;
2741 if (*size > count) return STATUS_BUFFER_TOO_SMALL;
2743 /* start from the end since HWND is potentially larger than user_handle_t */
2744 for (i = *size - 2; i >= 0; i--)
2745 buffer[i] = wine_server_ptr_handle( list[i] );
2746 buffer[*size - 1] = HWND_BOTTOM;
2747 return STATUS_SUCCESS;
2750 /***********************************************************************
2751 * NtUserFindWindowEx (USER32.@)
2753 HWND WINAPI NtUserFindWindowEx( HWND parent, HWND child, UNICODE_STRING *class, UNICODE_STRING *title,
2754 ULONG unk )
2756 HWND *list;
2757 HWND retvalue = 0;
2758 int i = 0, len = 0, title_len;
2759 WCHAR *buffer = NULL;
2761 if (!parent && child) parent = get_desktop_window();
2762 else if (parent == HWND_MESSAGE) parent = get_hwnd_message_parent();
2764 if (title)
2766 len = title->Length / sizeof(WCHAR) + 1; /* one extra char to check for chars beyond the end */
2767 if (!(buffer = malloc( (len + 1) * sizeof(WCHAR) ))) return 0;
2770 if (!(list = list_window_children( 0, parent, class, 0 ))) goto done;
2772 if (child)
2774 child = get_full_window_handle( child );
2775 while (list[i] && list[i] != child) i++;
2776 if (!list[i]) goto done;
2777 i++; /* start from next window */
2780 if (title)
2782 while (list[i])
2784 title_len = NtUserInternalGetWindowText( list[i], buffer, len + 1 );
2785 if (title_len * sizeof(WCHAR) == title->Length &&
2786 (!title_len || !wcsnicmp( buffer, title->Buffer, title_len )))
2787 break;
2788 i++;
2791 retvalue = list[i];
2793 done:
2794 free( list );
2795 free( buffer );
2796 return retvalue;
2799 /* Retrieve the window text from the server. */
2800 static data_size_t get_server_window_text( HWND hwnd, WCHAR *text, data_size_t count )
2802 data_size_t len = 0, needed = 0;
2804 SERVER_START_REQ( get_window_text )
2806 req->handle = wine_server_user_handle( hwnd );
2807 if (count) wine_server_set_reply( req, text, (count - 1) * sizeof(WCHAR) );
2808 if (!wine_server_call_err( req ))
2810 needed = reply->length;
2811 len = wine_server_reply_size(reply);
2814 SERVER_END_REQ;
2815 if (text) text[len / sizeof(WCHAR)] = 0;
2816 return needed;
2819 /*******************************************************************
2820 * NtUserInternalGetWindowText (win32u.@)
2822 INT WINAPI NtUserInternalGetWindowText( HWND hwnd, WCHAR *text, INT count )
2824 WND *win;
2826 if (count <= 0) return 0;
2827 if (!(win = get_win_ptr( hwnd ))) return 0;
2828 if (win == WND_DESKTOP) text[0] = 0;
2829 else if (win != WND_OTHER_PROCESS)
2831 if (win->text) lstrcpynW( text, win->text, count );
2832 else text[0] = 0;
2833 release_win_ptr( win );
2835 else
2837 get_server_window_text( hwnd, text, count );
2839 return lstrlenW(text);
2842 /*******************************************************************
2843 * get_windows_offset
2845 * Calculate the offset between the origin of the two windows. Used
2846 * to implement MapWindowPoints.
2848 static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, UINT dpi, BOOL *mirrored, POINT *ret_offset )
2850 WND *win;
2851 POINT offset;
2852 BOOL mirror_from, mirror_to, ret;
2853 HWND hwnd;
2855 offset.x = offset.y = 0;
2856 *mirrored = mirror_from = mirror_to = FALSE;
2858 /* Translate source window origin to screen coords */
2859 if (hwnd_from)
2861 if (!(win = get_win_ptr( hwnd_from )))
2863 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2864 return FALSE;
2866 if (win == WND_OTHER_PROCESS) goto other_process;
2867 if (win != WND_DESKTOP)
2869 UINT dpi_from = dpi ? dpi : get_win_monitor_dpi( hwnd_from );
2870 if (win->dwExStyle & WS_EX_LAYOUTRTL)
2872 mirror_from = TRUE;
2873 offset.x += win->client_rect.right - win->client_rect.left;
2875 while (win->parent)
2877 offset.x += win->client_rect.left;
2878 offset.y += win->client_rect.top;
2879 hwnd = win->parent;
2880 release_win_ptr( win );
2881 if (!(win = get_win_ptr( hwnd ))) break;
2882 if (win == WND_OTHER_PROCESS) goto other_process;
2883 if (win == WND_DESKTOP) break;
2884 if (win->flags & WIN_CHILDREN_MOVED)
2886 release_win_ptr( win );
2887 goto other_process;
2890 if (win && win != WND_DESKTOP) release_win_ptr( win );
2891 offset = map_dpi_point( offset, get_dpi_for_window( hwnd_from ), dpi_from );
2895 /* Translate origin to destination window coords */
2896 if (hwnd_to)
2898 if (!(win = get_win_ptr( hwnd_to )))
2900 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2901 return FALSE;
2903 if (win == WND_OTHER_PROCESS) goto other_process;
2904 if (win != WND_DESKTOP)
2906 UINT dpi_to = dpi ? dpi : get_win_monitor_dpi( hwnd_to );
2907 POINT pt = { 0, 0 };
2908 if (win->dwExStyle & WS_EX_LAYOUTRTL)
2910 mirror_to = TRUE;
2911 pt.x += win->client_rect.right - win->client_rect.left;
2913 while (win->parent)
2915 pt.x += win->client_rect.left;
2916 pt.y += win->client_rect.top;
2917 hwnd = win->parent;
2918 release_win_ptr( win );
2919 if (!(win = get_win_ptr( hwnd ))) break;
2920 if (win == WND_OTHER_PROCESS) goto other_process;
2921 if (win == WND_DESKTOP) break;
2922 if (win->flags & WIN_CHILDREN_MOVED)
2924 release_win_ptr( win );
2925 goto other_process;
2928 if (win && win != WND_DESKTOP) release_win_ptr( win );
2929 pt = map_dpi_point( pt, get_dpi_for_window( hwnd_to ), dpi_to );
2930 offset.x -= pt.x;
2931 offset.y -= pt.y;
2935 *mirrored = mirror_from ^ mirror_to;
2936 if (mirror_from) offset.x = -offset.x;
2937 *ret_offset = offset;
2938 return TRUE;
2940 other_process: /* one of the parents may belong to another process, do it the hard way */
2941 SERVER_START_REQ( get_windows_offset )
2943 req->from = wine_server_user_handle( hwnd_from );
2944 req->to = wine_server_user_handle( hwnd_to );
2945 req->dpi = dpi;
2946 if ((ret = !wine_server_call_err( req )))
2948 ret_offset->x = reply->x;
2949 ret_offset->y = reply->y;
2950 *mirrored = reply->mirror;
2953 SERVER_END_REQ;
2954 return ret;
2957 /* see ClientToScreen */
2958 BOOL client_to_screen( HWND hwnd, POINT *pt )
2960 POINT offset;
2961 BOOL mirrored;
2963 if (!hwnd)
2965 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2966 return FALSE;
2969 if (!get_windows_offset( hwnd, 0, get_thread_dpi(), &mirrored, &offset )) return FALSE;
2970 pt->x += offset.x;
2971 pt->y += offset.y;
2972 if (mirrored) pt->x = -pt->x;
2973 return TRUE;
2976 /* see ScreenToClient */
2977 BOOL screen_to_client( HWND hwnd, POINT *pt )
2979 POINT offset;
2980 BOOL mirrored;
2982 if (!hwnd)
2984 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2985 return FALSE;
2987 if (!get_windows_offset( 0, hwnd, get_thread_dpi(), &mirrored, &offset )) return FALSE;
2988 pt->x += offset.x;
2989 pt->y += offset.y;
2990 if (mirrored) pt->x = -pt->x;
2991 return TRUE;
2994 /* map coordinates of a window region */
2995 void map_window_region( HWND from, HWND to, HRGN hrgn )
2997 BOOL mirrored;
2998 POINT offset;
2999 UINT i, size;
3000 RGNDATA *data;
3001 HRGN new_rgn;
3002 RECT *rect;
3004 if (!get_windows_offset( from, to, get_thread_dpi(), &mirrored, &offset )) return;
3006 if (!mirrored)
3008 NtGdiOffsetRgn( hrgn, offset.x, offset.y );
3009 return;
3011 if (!(size = NtGdiGetRegionData( hrgn, 0, NULL ))) return;
3012 if (!(data = malloc( size ))) return;
3013 NtGdiGetRegionData( hrgn, size, data );
3014 rect = (RECT *)data->Buffer;
3015 for (i = 0; i < data->rdh.nCount; i++)
3017 int tmp = -(rect[i].left + offset.x);
3018 rect[i].left = -(rect[i].right + offset.x);
3019 rect[i].right = tmp;
3020 rect[i].top += offset.y;
3021 rect[i].bottom += offset.y;
3023 if ((new_rgn = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data )))
3025 NtGdiCombineRgn( hrgn, new_rgn, 0, RGN_COPY );
3026 NtGdiDeleteObjectApp( new_rgn );
3028 free( data );
3031 /* see MapWindowPoints */
3032 int map_window_points( HWND hwnd_from, HWND hwnd_to, POINT *points, UINT count, UINT dpi )
3034 BOOL mirrored;
3035 POINT offset;
3036 UINT i;
3038 if (!get_windows_offset( hwnd_from, hwnd_to, dpi, &mirrored, &offset )) return 0;
3040 for (i = 0; i < count; i++)
3042 points[i].x += offset.x;
3043 points[i].y += offset.y;
3044 if (mirrored) points[i].x = -points[i].x;
3046 if (mirrored && count == 2) /* special case for rectangle */
3048 int tmp = points[0].x;
3049 points[0].x = points[1].x;
3050 points[1].x = tmp;
3052 return MAKELONG( LOWORD(offset.x), LOWORD(offset.y) );
3055 /***********************************************************************
3056 * dump_winpos_flags
3058 static void dump_winpos_flags( UINT flags )
3060 static const UINT dumped_flags = (SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW |
3061 SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_SHOWWINDOW |
3062 SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOOWNERZORDER |
3063 SWP_NOSENDCHANGING | SWP_DEFERERASE | SWP_ASYNCWINDOWPOS |
3064 SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE | SWP_STATECHANGED);
3065 TRACE( "flags:" );
3066 if(flags & SWP_NOSIZE) TRACE( " SWP_NOSIZE" );
3067 if(flags & SWP_NOMOVE) TRACE( " SWP_NOMOVE" );
3068 if(flags & SWP_NOZORDER) TRACE( " SWP_NOZORDER" );
3069 if(flags & SWP_NOREDRAW) TRACE( " SWP_NOREDRAW" );
3070 if(flags & SWP_NOACTIVATE) TRACE( " SWP_NOACTIVATE" );
3071 if(flags & SWP_FRAMECHANGED) TRACE( " SWP_FRAMECHANGED" );
3072 if(flags & SWP_SHOWWINDOW) TRACE( " SWP_SHOWWINDOW" );
3073 if(flags & SWP_HIDEWINDOW) TRACE( " SWP_HIDEWINDOW" );
3074 if(flags & SWP_NOCOPYBITS) TRACE( " SWP_NOCOPYBITS" );
3075 if(flags & SWP_NOOWNERZORDER) TRACE( " SWP_NOOWNERZORDER" );
3076 if(flags & SWP_NOSENDCHANGING) TRACE( " SWP_NOSENDCHANGING" );
3077 if(flags & SWP_DEFERERASE) TRACE( " SWP_DEFERERASE" );
3078 if(flags & SWP_ASYNCWINDOWPOS) TRACE( " SWP_ASYNCWINDOWPOS" );
3079 if(flags & SWP_NOCLIENTSIZE) TRACE( " SWP_NOCLIENTSIZE" );
3080 if(flags & SWP_NOCLIENTMOVE) TRACE( " SWP_NOCLIENTMOVE" );
3081 if(flags & SWP_STATECHANGED) TRACE( " SWP_STATECHANGED" );
3083 if(flags & ~dumped_flags) TRACE( " %08x", flags & ~dumped_flags );
3084 TRACE( "\n" );
3087 /***********************************************************************
3088 * map_dpi_winpos
3090 static void map_dpi_winpos( WINDOWPOS *winpos )
3092 UINT dpi_from = get_thread_dpi();
3093 UINT dpi_to = get_dpi_for_window( winpos->hwnd );
3095 if (!dpi_from) dpi_from = get_win_monitor_dpi( winpos->hwnd );
3096 if (dpi_from == dpi_to) return;
3097 winpos->x = muldiv( winpos->x, dpi_to, dpi_from );
3098 winpos->y = muldiv( winpos->y, dpi_to, dpi_from );
3099 winpos->cx = muldiv( winpos->cx, dpi_to, dpi_from );
3100 winpos->cy = muldiv( winpos->cy, dpi_to, dpi_from );
3103 /***********************************************************************
3104 * calc_winpos
3106 static BOOL calc_winpos( WINDOWPOS *winpos, RECT *old_window_rect, RECT *old_client_rect,
3107 RECT *new_window_rect, RECT *new_client_rect )
3109 WND *win;
3111 /* Send WM_WINDOWPOSCHANGING message */
3112 if (!(winpos->flags & SWP_NOSENDCHANGING)
3113 && !((winpos->flags & SWP_AGG_NOCLIENTCHANGE) && (winpos->flags & SWP_SHOWWINDOW)))
3114 send_message( winpos->hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)winpos );
3116 if (!(win = get_win_ptr( winpos->hwnd )) ||
3117 win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
3119 /* Calculate new position and size */
3120 get_window_rects( winpos->hwnd, COORDS_PARENT, old_window_rect, old_client_rect, get_thread_dpi() );
3121 *new_window_rect = *old_window_rect;
3122 *new_client_rect = *old_client_rect;
3124 if (!(winpos->flags & SWP_NOSIZE))
3126 if (win->dwStyle & WS_MINIMIZE)
3128 new_window_rect->right = new_window_rect->left + get_system_metrics( SM_CXMINIMIZED );
3129 new_window_rect->bottom = new_window_rect->top + get_system_metrics( SM_CYMINIMIZED );
3131 else
3133 new_window_rect->right = new_window_rect->left + winpos->cx;
3134 new_window_rect->bottom = new_window_rect->top + winpos->cy;
3138 if (!(winpos->flags & SWP_NOMOVE))
3140 /* If the window is toplevel minimized off-screen, force keep it there */
3141 if ((win->dwStyle & WS_MINIMIZE) &&
3142 win->window_rect.left <= -32000 && win->window_rect.top <= -32000 &&
3143 (!win->parent || win->parent == get_desktop_window()))
3145 winpos->x = -32000;
3146 winpos->y = -32000;
3148 new_window_rect->left = winpos->x;
3149 new_window_rect->top = winpos->y;
3150 new_window_rect->right += winpos->x - old_window_rect->left;
3151 new_window_rect->bottom += winpos->y - old_window_rect->top;
3153 OffsetRect( new_client_rect, winpos->x - old_window_rect->left,
3154 winpos->y - old_window_rect->top );
3156 winpos->flags |= SWP_NOCLIENTMOVE | SWP_NOCLIENTSIZE;
3158 TRACE( "hwnd %p, after %p, swp %d,%d %dx%d flags %08x current %s style %08x new %s\n",
3159 winpos->hwnd, winpos->hwndInsertAfter, winpos->x, winpos->y,
3160 winpos->cx, winpos->cy, winpos->flags,
3161 wine_dbgstr_rect( old_window_rect ), win->dwStyle,
3162 wine_dbgstr_rect( new_window_rect ));
3164 release_win_ptr( win );
3165 return TRUE;
3168 /***********************************************************************
3169 * get_valid_rects
3171 * Compute the valid rects from the old and new client rect and WVR_* flags.
3172 * Helper for WM_NCCALCSIZE handling.
3174 static inline void get_valid_rects( const RECT *old_client, const RECT *new_client, UINT flags,
3175 RECT *valid )
3177 int cx, cy;
3179 if (flags & WVR_REDRAW)
3181 SetRectEmpty( &valid[0] );
3182 SetRectEmpty( &valid[1] );
3183 return;
3186 if (flags & WVR_VALIDRECTS)
3188 if (!intersect_rect( &valid[0], &valid[0], new_client ) ||
3189 !intersect_rect( &valid[1], &valid[1], old_client ))
3191 SetRectEmpty( &valid[0] );
3192 SetRectEmpty( &valid[1] );
3193 return;
3195 flags = WVR_ALIGNLEFT | WVR_ALIGNTOP;
3197 else
3199 valid[0] = *new_client;
3200 valid[1] = *old_client;
3203 /* make sure the rectangles have the same size */
3204 cx = min( valid[0].right - valid[0].left, valid[1].right - valid[1].left );
3205 cy = min( valid[0].bottom - valid[0].top, valid[1].bottom - valid[1].top );
3207 if (flags & WVR_ALIGNBOTTOM)
3209 valid[0].top = valid[0].bottom - cy;
3210 valid[1].top = valid[1].bottom - cy;
3212 else
3214 valid[0].bottom = valid[0].top + cy;
3215 valid[1].bottom = valid[1].top + cy;
3217 if (flags & WVR_ALIGNRIGHT)
3219 valid[0].left = valid[0].right - cx;
3220 valid[1].left = valid[1].right - cx;
3222 else
3224 valid[0].right = valid[0].left + cx;
3225 valid[1].right = valid[1].left + cx;
3229 static UINT calc_ncsize( WINDOWPOS *winpos, const RECT *old_window_rect, const RECT *old_client_rect,
3230 const RECT *new_window_rect, RECT *new_client_rect, RECT *valid_rects,
3231 int parent_x, int parent_y )
3233 UINT wvr_flags = 0;
3235 /* Send WM_NCCALCSIZE message to get new client area */
3236 if ((winpos->flags & (SWP_FRAMECHANGED | SWP_NOSIZE)) != SWP_NOSIZE)
3238 NCCALCSIZE_PARAMS params;
3239 WINDOWPOS winposCopy;
3240 UINT class_style;
3242 params.rgrc[0] = *new_window_rect;
3243 params.rgrc[1] = *old_window_rect;
3244 params.rgrc[2] = *old_client_rect;
3245 params.lppos = &winposCopy;
3246 winposCopy = *winpos;
3248 if (winpos->flags & SWP_NOMOVE)
3250 winposCopy.x = old_window_rect->left;
3251 winposCopy.y = old_window_rect->top;
3254 if (winpos->flags & SWP_NOSIZE)
3256 winposCopy.cx = old_window_rect->right - old_window_rect->left;
3257 winposCopy.cy = old_window_rect->bottom - old_window_rect->top;
3260 class_style = get_class_long( winpos->hwnd, GCL_STYLE, FALSE );
3261 if (class_style & CS_VREDRAW) wvr_flags |= WVR_VREDRAW;
3262 if (class_style & CS_HREDRAW) wvr_flags |= WVR_HREDRAW;
3264 wvr_flags |= send_message( winpos->hwnd, WM_NCCALCSIZE, TRUE, (LPARAM)&params );
3266 *new_client_rect = params.rgrc[0];
3268 TRACE( "hwnd %p old win %s old client %s new win %s new client %s\n", winpos->hwnd,
3269 wine_dbgstr_rect(old_window_rect), wine_dbgstr_rect(old_client_rect),
3270 wine_dbgstr_rect(new_window_rect), wine_dbgstr_rect(new_client_rect) );
3272 if (new_client_rect->left != old_client_rect->left - parent_x ||
3273 new_client_rect->top != old_client_rect->top - parent_y)
3274 winpos->flags &= ~SWP_NOCLIENTMOVE;
3276 if ((new_client_rect->right - new_client_rect->left !=
3277 old_client_rect->right - old_client_rect->left))
3278 winpos->flags &= ~SWP_NOCLIENTSIZE;
3279 else
3280 wvr_flags &= ~WVR_HREDRAW;
3282 if (new_client_rect->bottom - new_client_rect->top !=
3283 old_client_rect->bottom - old_client_rect->top)
3284 winpos->flags &= ~SWP_NOCLIENTSIZE;
3285 else
3286 wvr_flags &= ~WVR_VREDRAW;
3288 valid_rects[0] = params.rgrc[1];
3289 valid_rects[1] = params.rgrc[2];
3291 else
3293 if (!(winpos->flags & SWP_NOMOVE) &&
3294 (new_client_rect->left != old_client_rect->left - parent_x ||
3295 new_client_rect->top != old_client_rect->top - parent_y))
3296 winpos->flags &= ~SWP_NOCLIENTMOVE;
3299 if (winpos->flags & (SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_SHOWWINDOW | SWP_HIDEWINDOW))
3301 SetRectEmpty( &valid_rects[0] );
3302 SetRectEmpty( &valid_rects[1] );
3304 else get_valid_rects( old_client_rect, new_client_rect, wvr_flags, valid_rects );
3306 return wvr_flags;
3309 /* fix redundant flags and values in the WINDOWPOS structure */
3310 static BOOL fixup_swp_flags( WINDOWPOS *winpos, const RECT *old_window_rect, int parent_x, int parent_y )
3312 HWND parent;
3313 WND *win = get_win_ptr( winpos->hwnd );
3314 BOOL ret = TRUE;
3316 if (!win || win == WND_OTHER_PROCESS)
3318 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
3319 return FALSE;
3321 winpos->hwnd = win->obj.handle; /* make it a full handle */
3323 /* Finally make sure that all coordinates are valid */
3324 if (winpos->x < -32768) winpos->x = -32768;
3325 else if (winpos->x > 32767) winpos->x = 32767;
3326 if (winpos->y < -32768) winpos->y = -32768;
3327 else if (winpos->y > 32767) winpos->y = 32767;
3329 if (winpos->cx < 0) winpos->cx = 0;
3330 else if (winpos->cx > 32767) winpos->cx = 32767;
3331 if (winpos->cy < 0) winpos->cy = 0;
3332 else if (winpos->cy > 32767) winpos->cy = 32767;
3334 parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3335 if (!is_window_visible( parent )) winpos->flags |= SWP_NOREDRAW;
3337 if (win->dwStyle & WS_VISIBLE) winpos->flags &= ~SWP_SHOWWINDOW;
3338 else
3340 winpos->flags &= ~SWP_HIDEWINDOW;
3341 if (!(winpos->flags & SWP_SHOWWINDOW)) winpos->flags |= SWP_NOREDRAW;
3344 if ((old_window_rect->right - old_window_rect->left == winpos->cx) &&
3345 (old_window_rect->bottom - old_window_rect->top == winpos->cy))
3346 winpos->flags |= SWP_NOSIZE; /* Already the right size */
3348 if ((old_window_rect->left - parent_x == winpos->x) && (old_window_rect->top - parent_y == winpos->y))
3349 winpos->flags |= SWP_NOMOVE; /* Already the right position */
3351 if ((win->dwStyle & (WS_POPUP | WS_CHILD)) != WS_CHILD)
3353 if (!(winpos->flags & (SWP_NOACTIVATE|SWP_HIDEWINDOW)) && /* Bring to the top when activating */
3354 (winpos->flags & SWP_NOZORDER ||
3355 (winpos->hwndInsertAfter != HWND_TOPMOST && winpos->hwndInsertAfter != HWND_NOTOPMOST)))
3357 winpos->flags &= ~SWP_NOZORDER;
3358 winpos->hwndInsertAfter = HWND_TOP;
3362 /* Check hwndInsertAfter */
3363 if (winpos->flags & SWP_NOZORDER) goto done;
3365 if (winpos->hwndInsertAfter == HWND_TOP)
3367 if (get_window_relative( winpos->hwnd, GW_HWNDFIRST ) == winpos->hwnd)
3368 winpos->flags |= SWP_NOZORDER;
3370 else if (winpos->hwndInsertAfter == HWND_BOTTOM)
3372 if (!(win->dwExStyle & WS_EX_TOPMOST) &&
3373 get_window_relative( winpos->hwnd, GW_HWNDLAST ) == winpos->hwnd)
3374 winpos->flags |= SWP_NOZORDER;
3376 else if (winpos->hwndInsertAfter == HWND_TOPMOST)
3378 if ((win->dwExStyle & WS_EX_TOPMOST) &&
3379 get_window_relative( winpos->hwnd, GW_HWNDFIRST ) == winpos->hwnd)
3380 winpos->flags |= SWP_NOZORDER;
3382 else if (winpos->hwndInsertAfter == HWND_NOTOPMOST)
3384 if (!(win->dwExStyle & WS_EX_TOPMOST))
3385 winpos->flags |= SWP_NOZORDER;
3387 else
3389 if ((winpos->hwnd == winpos->hwndInsertAfter) ||
3390 (winpos->hwnd == get_window_relative( winpos->hwndInsertAfter, GW_HWNDNEXT )))
3391 winpos->flags |= SWP_NOZORDER;
3393 done:
3394 release_win_ptr( win );
3395 return ret;
3398 /***********************************************************************
3399 * swp_owner_popups
3401 * fix Z order taking into account owned popups -
3402 * basically we need to maintain them above the window that owns them
3404 * FIXME: hide/show owned popups when owner visibility changes.
3406 static HWND swp_owner_popups( HWND hwnd, HWND after )
3408 HWND owner, *list = NULL;
3409 unsigned int i;
3411 TRACE( "(%p) after = %p\n", hwnd, after );
3413 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) return after;
3415 if ((owner = get_window_relative( hwnd, GW_OWNER )))
3417 /* make sure this popup stays above the owner */
3419 if (after != HWND_TOPMOST)
3421 if (!(list = list_window_children( 0, get_desktop_window(), NULL, 0 ))) return after;
3423 for (i = 0; list[i]; i++)
3425 BOOL topmost = (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TOPMOST) != 0;
3427 if (list[i] == owner)
3429 if (i > 0) after = list[i-1];
3430 else after = topmost ? HWND_TOPMOST : HWND_TOP;
3431 break;
3434 if (after == HWND_TOP || after == HWND_NOTOPMOST)
3436 if (!topmost) break;
3438 else if (list[i] == after) break;
3443 if (after == HWND_BOTTOM) goto done;
3444 if (!list && !(list = list_window_children( 0, get_desktop_window(), NULL, 0 ))) goto done;
3446 i = 0;
3447 if (after == HWND_TOP || after == HWND_NOTOPMOST)
3449 if (after == HWND_NOTOPMOST ||
3450 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST))
3452 /* skip all the topmost windows */
3453 while (list[i] && (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TOPMOST)) i++;
3456 else if (after != HWND_TOPMOST)
3458 /* skip windows that are already placed correctly */
3459 for (i = 0; list[i]; i++)
3461 if (list[i] == after) break;
3462 if (list[i] == hwnd) goto done; /* nothing to do if window is moving backwards in z-order */
3466 for ( ; list[i]; i++)
3468 if (list[i] == hwnd) break;
3469 if (get_window_relative( list[i], GW_OWNER ) != hwnd) continue;
3470 TRACE( "moving %p owned by %p after %p\n", list[i], hwnd, after );
3471 NtUserSetWindowPos( list[i], after, 0, 0, 0, 0,
3472 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_DEFERERASE );
3473 after = list[i];
3476 done:
3477 free( list );
3478 return after;
3481 /* NtUserSetWindowPos implementation */
3482 BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y )
3484 RECT old_window_rect, old_client_rect, new_window_rect, new_client_rect, valid_rects[2], visible_rect, surface_rect;
3485 struct window_surface *surface;
3486 UINT orig_flags, context;
3487 BOOL ret = FALSE;
3489 orig_flags = winpos->flags;
3491 /* First, check z-order arguments. */
3492 if (!(winpos->flags & SWP_NOZORDER))
3494 /* fix sign extension */
3495 if (winpos->hwndInsertAfter == (HWND)0xffff) winpos->hwndInsertAfter = HWND_TOPMOST;
3496 else if (winpos->hwndInsertAfter == (HWND)0xfffe) winpos->hwndInsertAfter = HWND_NOTOPMOST;
3498 if (!(winpos->hwndInsertAfter == HWND_TOP ||
3499 winpos->hwndInsertAfter == HWND_BOTTOM ||
3500 winpos->hwndInsertAfter == HWND_TOPMOST ||
3501 winpos->hwndInsertAfter == HWND_NOTOPMOST))
3503 HWND parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3504 HWND insertafter_parent = NtUserGetAncestor( winpos->hwndInsertAfter, GA_PARENT );
3506 /* hwndInsertAfter must be a sibling of the window */
3507 if (!insertafter_parent) return FALSE;
3508 if (insertafter_parent != parent) return TRUE;
3512 /* Make sure that coordinates are valid for WM_WINDOWPOSCHANGING */
3513 if (!(winpos->flags & SWP_NOMOVE))
3515 if (winpos->x < -32768) winpos->x = -32768;
3516 else if (winpos->x > 32767) winpos->x = 32767;
3517 if (winpos->y < -32768) winpos->y = -32768;
3518 else if (winpos->y > 32767) winpos->y = 32767;
3520 if (!(winpos->flags & SWP_NOSIZE))
3522 if (winpos->cx < 0) winpos->cx = 0;
3523 else if (winpos->cx > 32767) winpos->cx = 32767;
3524 if (winpos->cy < 0) winpos->cy = 0;
3525 else if (winpos->cy > 32767) winpos->cy = 32767;
3528 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( winpos->hwnd ));
3530 if (!calc_winpos( winpos, &old_window_rect, &old_client_rect,
3531 &new_window_rect, &new_client_rect )) goto done;
3533 /* Fix redundant flags */
3534 if (!fixup_swp_flags( winpos, &old_window_rect, parent_x, parent_y )) goto done;
3536 if((winpos->flags & (SWP_NOZORDER | SWP_HIDEWINDOW | SWP_SHOWWINDOW)) != SWP_NOZORDER)
3538 if (NtUserGetAncestor( winpos->hwnd, GA_PARENT ) == get_desktop_window())
3539 winpos->hwndInsertAfter = swp_owner_popups( winpos->hwnd, winpos->hwndInsertAfter );
3542 /* Common operations */
3544 calc_ncsize( winpos, &old_window_rect, &old_client_rect,
3545 &new_window_rect, &new_client_rect, valid_rects, parent_x, parent_y );
3547 surface = create_window_surface( winpos->hwnd, winpos->flags, FALSE, &new_window_rect, &new_client_rect,
3548 &visible_rect, &surface_rect );
3549 if (!apply_window_pos( winpos->hwnd, winpos->hwndInsertAfter, winpos->flags, surface,
3550 &new_window_rect, &new_client_rect, &visible_rect, valid_rects ))
3552 if (surface) window_surface_release( surface );
3553 goto done;
3555 if (surface) window_surface_release( surface );
3557 if (winpos->flags & SWP_HIDEWINDOW)
3559 NtUserNotifyWinEvent( EVENT_OBJECT_HIDE, winpos->hwnd, 0, 0 );
3561 NtUserHideCaret( winpos->hwnd );
3563 else if (winpos->flags & SWP_SHOWWINDOW)
3565 NtUserNotifyWinEvent( EVENT_OBJECT_SHOW, winpos->hwnd, 0, 0 );
3567 NtUserShowCaret( winpos->hwnd );
3570 if (!(winpos->flags & (SWP_NOACTIVATE|SWP_HIDEWINDOW)))
3572 /* child windows get WM_CHILDACTIVATE message */
3573 if ((get_window_long( winpos->hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD)
3574 send_message( winpos->hwnd, WM_CHILDACTIVATE, 0, 0 );
3575 else
3576 set_foreground_window( winpos->hwnd, FALSE );
3579 if(!(orig_flags & SWP_DEFERERASE))
3581 /* erase parent when hiding or resizing child */
3582 if ((orig_flags & SWP_HIDEWINDOW) ||
3583 (!(orig_flags & SWP_SHOWWINDOW) &&
3584 (winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOGEOMETRYCHANGE))
3586 HWND parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3587 if (!parent || parent == get_desktop_window()) parent = winpos->hwnd;
3588 erase_now( parent, 0 );
3591 /* Give newly shown windows a chance to redraw */
3592 if(((winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOPOSCHANGE)
3593 && !(orig_flags & SWP_AGG_NOCLIENTCHANGE) && (orig_flags & SWP_SHOWWINDOW))
3595 erase_now(winpos->hwnd, 0);
3599 /* And last, send the WM_WINDOWPOSCHANGED message */
3601 TRACE( "\tstatus flags = %04x\n", winpos->flags & SWP_AGG_STATUSFLAGS );
3603 if (((winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOPOSCHANGE)
3604 && !((orig_flags & SWP_AGG_NOCLIENTCHANGE) && (orig_flags & SWP_SHOWWINDOW)))
3606 /* WM_WINDOWPOSCHANGED is sent even if SWP_NOSENDCHANGING is set
3607 and always contains final window position.
3609 winpos->x = new_window_rect.left;
3610 winpos->y = new_window_rect.top;
3611 winpos->cx = new_window_rect.right - new_window_rect.left;
3612 winpos->cy = new_window_rect.bottom - new_window_rect.top;
3613 send_message( winpos->hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)winpos );
3616 if ((winpos->flags & (SWP_NOSIZE|SWP_NOMOVE|SWP_FRAMECHANGED)) != (SWP_NOSIZE|SWP_NOMOVE))
3617 NtUserNotifyWinEvent( EVENT_OBJECT_LOCATIONCHANGE, winpos->hwnd, OBJID_WINDOW, 0 );
3619 ret = TRUE;
3620 done:
3621 set_thread_dpi_awareness_context( context );
3622 return ret;
3625 /*******************************************************************
3626 * NtUserSetWindowPos (win32u.@)
3628 BOOL WINAPI NtUserSetWindowPos( HWND hwnd, HWND after, INT x, INT y, INT cx, INT cy, UINT flags )
3630 WINDOWPOS winpos;
3632 TRACE( "hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n", hwnd, after, x, y, cx, cy, flags );
3633 if(TRACE_ON(win)) dump_winpos_flags(flags);
3635 if (is_broadcast( hwnd ))
3637 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
3638 return FALSE;
3641 winpos.hwnd = get_full_window_handle( hwnd );
3642 winpos.hwndInsertAfter = get_full_window_handle( after );
3643 winpos.x = x;
3644 winpos.y = y;
3645 winpos.cx = cx;
3646 winpos.cy = cy;
3647 winpos.flags = flags;
3649 map_dpi_winpos( &winpos );
3651 if (is_current_thread_window( hwnd ))
3652 return set_window_pos( &winpos, 0, 0 );
3654 if (flags & SWP_ASYNCWINDOWPOS)
3655 return NtUserMessageCall( winpos.hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)&winpos,
3656 0, NtUserSendNotifyMessage, FALSE );
3657 else
3658 return send_message( winpos.hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)&winpos );
3661 typedef struct
3663 struct user_object obj;
3664 INT count;
3665 INT suggested_count;
3666 HWND parent;
3667 WINDOWPOS *winpos;
3668 } DWP;
3670 /* see BeginDeferWindowPos */
3671 HDWP begin_defer_window_pos( INT count )
3673 HDWP handle = 0;
3674 DWP *dwp;
3676 TRACE( "%d\n", count );
3678 if (count < 0)
3680 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
3681 return 0;
3683 /* Windows allows zero count, in which case it allocates context for 8 moves */
3684 if (count == 0) count = 8;
3686 if (!(dwp = malloc( sizeof(DWP) ))) return 0;
3688 dwp->count = 0;
3689 dwp->parent = 0;
3690 dwp->suggested_count = count;
3692 if (!(dwp->winpos = malloc( count * sizeof(WINDOWPOS) )) ||
3693 !(handle = alloc_user_handle( &dwp->obj, NTUSER_OBJ_WINPOS )))
3695 free( dwp->winpos );
3696 free( dwp );
3699 TRACE( "returning %p\n", handle );
3700 return handle;
3703 /***********************************************************************
3704 * NtUserDeferWindowPosAndBand (win32u.@)
3706 HDWP WINAPI NtUserDeferWindowPosAndBand( HDWP hdwp, HWND hwnd, HWND after,
3707 INT x, INT y, INT cx, INT cy,
3708 UINT flags, UINT unk1, UINT unk2 )
3710 HDWP retvalue = hdwp;
3711 WINDOWPOS winpos;
3712 DWP *dwp;
3713 int i;
3715 TRACE( "hdwp %p, hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n",
3716 hdwp, hwnd, after, x, y, cx, cy, flags );
3718 winpos.hwnd = get_full_window_handle( hwnd );
3719 if (is_desktop_window( winpos.hwnd ) || !is_window( winpos.hwnd ))
3721 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
3722 return 0;
3725 winpos.hwndInsertAfter = get_full_window_handle( after );
3726 winpos.flags = flags;
3727 winpos.x = x;
3728 winpos.y = y;
3729 winpos.cx = cx;
3730 winpos.cy = cy;
3731 map_dpi_winpos( &winpos );
3733 if (!(dwp = get_user_handle_ptr( hdwp, NTUSER_OBJ_WINPOS ))) return 0;
3734 if (dwp == OBJ_OTHER_PROCESS)
3736 FIXME( "other process handle %p\n", hdwp );
3737 return 0;
3740 for (i = 0; i < dwp->count; i++)
3742 if (dwp->winpos[i].hwnd == winpos.hwnd)
3744 /* Merge with the other changes */
3745 if (!(flags & SWP_NOZORDER))
3747 dwp->winpos[i].hwndInsertAfter = winpos.hwndInsertAfter;
3749 if (!(flags & SWP_NOMOVE))
3751 dwp->winpos[i].x = winpos.x;
3752 dwp->winpos[i].y = winpos.y;
3754 if (!(flags & SWP_NOSIZE))
3756 dwp->winpos[i].cx = winpos.cx;
3757 dwp->winpos[i].cy = winpos.cy;
3759 dwp->winpos[i].flags &= flags | ~(SWP_NOSIZE | SWP_NOMOVE |
3760 SWP_NOZORDER | SWP_NOREDRAW |
3761 SWP_NOACTIVATE | SWP_NOCOPYBITS|
3762 SWP_NOOWNERZORDER);
3763 dwp->winpos[i].flags |= flags & (SWP_SHOWWINDOW | SWP_HIDEWINDOW |
3764 SWP_FRAMECHANGED);
3765 goto done;
3768 if (dwp->count >= dwp->suggested_count)
3770 WINDOWPOS *newpos = realloc( dwp->winpos, dwp->suggested_count * 2 * sizeof(WINDOWPOS) );
3771 if (!newpos)
3773 retvalue = 0;
3774 goto done;
3776 dwp->suggested_count *= 2;
3777 dwp->winpos = newpos;
3779 dwp->winpos[dwp->count++] = winpos;
3780 done:
3781 release_user_handle_ptr( dwp );
3782 return retvalue;
3785 /***********************************************************************
3786 * NtUserEndDeferWindowPosEx (win32u.@)
3788 BOOL WINAPI NtUserEndDeferWindowPosEx( HDWP hdwp, BOOL async )
3790 WINDOWPOS *winpos;
3791 DWP *dwp;
3792 int i;
3794 TRACE( "%p\n", hdwp );
3796 if (async) FIXME( "async not supported\n" );
3798 if (!(dwp = free_user_handle( hdwp, NTUSER_OBJ_WINPOS ))) return FALSE;
3799 if (dwp == OBJ_OTHER_PROCESS)
3801 FIXME( "other process handle %p\n", hdwp );
3802 return FALSE;
3805 for (i = 0, winpos = dwp->winpos; i < dwp->count; i++, winpos++)
3807 TRACE( "hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n",
3808 winpos->hwnd, winpos->hwndInsertAfter, winpos->x, winpos->y,
3809 winpos->cx, winpos->cy, winpos->flags );
3811 if (is_current_thread_window( winpos->hwnd ))
3812 set_window_pos( winpos, 0, 0 );
3813 else
3814 send_message( winpos->hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)winpos );
3816 free( dwp->winpos );
3817 free( dwp );
3818 return TRUE;
3821 /***********************************************************************
3822 * NtUserSetInternalWindowPos (win32u.@)
3824 void WINAPI NtUserSetInternalWindowPos( HWND hwnd, UINT cmd, RECT *rect, POINT *pt )
3826 WINDOWPLACEMENT wndpl;
3827 UINT flags;
3829 wndpl.length = sizeof(wndpl);
3830 wndpl.showCmd = cmd;
3831 wndpl.flags = flags = 0;
3833 if (pt)
3835 flags |= PLACE_MIN;
3836 wndpl.flags |= WPF_SETMINPOSITION;
3837 wndpl.ptMinPosition = *pt;
3839 if( rect )
3841 flags |= PLACE_RECT;
3842 wndpl.rcNormalPosition = *rect;
3844 set_window_placement( hwnd, &wndpl, flags );
3847 /***********************************************************************
3848 * win_set_flags
3850 * Set the flags of a window and return the previous value.
3852 UINT win_set_flags( HWND hwnd, UINT set_mask, UINT clear_mask )
3854 WND *win = get_win_ptr( hwnd );
3855 UINT ret;
3857 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return 0;
3858 ret = win->flags;
3859 win->flags = (ret & ~clear_mask) | set_mask;
3860 release_win_ptr( win );
3861 return ret;
3864 /*******************************************************************
3865 * can_activate_window
3867 * Check if we can activate the specified window.
3869 static BOOL can_activate_window( HWND hwnd )
3871 LONG style;
3873 if (!hwnd) return FALSE;
3874 style = get_window_long( hwnd, GWL_STYLE );
3875 if (!(style & WS_VISIBLE)) return FALSE;
3876 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
3877 return !(style & WS_DISABLED);
3880 /*******************************************************************
3881 * activate_other_window
3883 * Activates window other than hwnd.
3885 static void activate_other_window( HWND hwnd )
3887 HWND hwnd_to, fg;
3889 if ((get_window_long( hwnd, GWL_STYLE ) & WS_POPUP) &&
3890 (hwnd_to = get_window_relative( hwnd, GW_OWNER )))
3892 hwnd_to = NtUserGetAncestor( hwnd_to, GA_ROOT );
3893 if (can_activate_window( hwnd_to )) goto done;
3896 hwnd_to = hwnd;
3897 for (;;)
3899 if (!(hwnd_to = get_window_relative( hwnd_to, GW_HWNDNEXT ))) break;
3900 if (can_activate_window( hwnd_to )) goto done;
3903 hwnd_to = get_window_relative( get_desktop_window(), GW_CHILD );
3904 for (;;)
3906 if (hwnd_to == hwnd)
3908 hwnd_to = 0;
3909 break;
3911 if (can_activate_window( hwnd_to )) goto done;
3912 if (!(hwnd_to = get_window_relative( hwnd_to, GW_HWNDNEXT ))) break;
3915 done:
3916 fg = NtUserGetForegroundWindow();
3917 TRACE( "win = %p fg = %p\n", hwnd_to, fg );
3918 if (!fg || hwnd == fg)
3920 if (set_foreground_window( hwnd_to, FALSE )) return;
3922 if (NtUserSetActiveWindow( hwnd_to )) NtUserSetActiveWindow( 0 );
3925 /*******************************************************************
3926 * send_parent_notify
3928 static void send_parent_notify( HWND hwnd, UINT msg )
3930 if ((get_window_long( hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD &&
3931 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY))
3933 HWND parent = get_parent( hwnd );
3934 if (parent && parent != get_desktop_window())
3935 send_message( parent, WM_PARENTNOTIFY,
3936 MAKEWPARAM( msg, get_window_long( hwnd, GWLP_ID )), (LPARAM)hwnd );
3940 /*******************************************************************
3941 * get_min_max_info
3943 * Get the minimized and maximized information for a window.
3945 MINMAXINFO get_min_max_info( HWND hwnd )
3947 LONG style = get_window_long( hwnd, GWL_STYLE );
3948 LONG exstyle = get_window_long( hwnd, GWL_EXSTYLE );
3949 UINT context;
3950 RECT rc_work, rc_primary;
3951 LONG adjusted_style;
3952 MINMAXINFO minmax;
3953 INT xinc, yinc;
3954 RECT rc;
3955 WND *win;
3957 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
3959 /* Compute default values */
3961 get_window_rect( hwnd, &rc, get_thread_dpi() );
3962 minmax.ptReserved.x = rc.left;
3963 minmax.ptReserved.y = rc.top;
3965 if ((style & WS_CAPTION) == WS_CAPTION)
3966 adjusted_style = style & ~WS_BORDER; /* WS_CAPTION = WS_DLGFRAME | WS_BORDER */
3967 else
3968 adjusted_style = style;
3970 get_client_rect( NtUserGetAncestor( hwnd, GA_PARENT ), &rc, get_thread_dpi() );
3971 adjust_window_rect( &rc, adjusted_style, (style & WS_POPUP) && get_menu( hwnd ), exstyle, get_system_dpi() );
3973 xinc = -rc.left;
3974 yinc = -rc.top;
3976 minmax.ptMaxSize.x = rc.right - rc.left;
3977 minmax.ptMaxSize.y = rc.bottom - rc.top;
3978 if (style & (WS_DLGFRAME | WS_BORDER))
3980 minmax.ptMinTrackSize.x = get_system_metrics( SM_CXMINTRACK );
3981 minmax.ptMinTrackSize.y = get_system_metrics( SM_CYMINTRACK );
3983 else
3985 minmax.ptMinTrackSize.x = 2 * xinc;
3986 minmax.ptMinTrackSize.y = 2 * yinc;
3988 minmax.ptMaxTrackSize.x = get_system_metrics( SM_CXMAXTRACK );
3989 minmax.ptMaxTrackSize.y = get_system_metrics( SM_CYMAXTRACK );
3990 minmax.ptMaxPosition.x = -xinc;
3991 minmax.ptMaxPosition.y = -yinc;
3993 if ((win = get_win_ptr( hwnd )) && win != WND_DESKTOP && win != WND_OTHER_PROCESS)
3995 if (!empty_point( win->max_pos )) minmax.ptMaxPosition = win->max_pos;
3996 release_win_ptr( win );
3999 send_message( hwnd, WM_GETMINMAXINFO, 0, (LPARAM)&minmax );
4001 /* if the app didn't change the values, adapt them for the current monitor */
4003 if (get_work_rect( hwnd, &rc_work ))
4005 rc_primary = get_primary_monitor_rect( get_thread_dpi() );
4006 if (minmax.ptMaxSize.x == (rc_primary.right - rc_primary.left) + 2 * xinc &&
4007 minmax.ptMaxSize.y == (rc_primary.bottom - rc_primary.top) + 2 * yinc)
4009 minmax.ptMaxSize.x = (rc_work.right - rc_work.left) + 2 * xinc;
4010 minmax.ptMaxSize.y = (rc_work.bottom - rc_work.top) + 2 * yinc;
4012 if (minmax.ptMaxPosition.x == -xinc && minmax.ptMaxPosition.y == -yinc)
4014 minmax.ptMaxPosition.x = rc_work.left - xinc;
4015 minmax.ptMaxPosition.y = rc_work.top - yinc;
4019 TRACE( "%d %d / %d %d / %d %d / %d %d\n",
4020 (int)minmax.ptMaxSize.x, (int)minmax.ptMaxSize.y,
4021 (int)minmax.ptMaxPosition.x, (int)minmax.ptMaxPosition.y,
4022 (int)minmax.ptMaxTrackSize.x, (int)minmax.ptMaxTrackSize.y,
4023 (int)minmax.ptMinTrackSize.x, (int)minmax.ptMinTrackSize.y );
4025 minmax.ptMaxTrackSize.x = max( minmax.ptMaxTrackSize.x, minmax.ptMinTrackSize.x );
4026 minmax.ptMaxTrackSize.y = max( minmax.ptMaxTrackSize.y, minmax.ptMinTrackSize.y );
4028 set_thread_dpi_awareness_context( context );
4029 return minmax;
4032 static POINT get_first_minimized_child_pos( const RECT *parent, const MINIMIZEDMETRICS *mm,
4033 int width, int height )
4035 POINT ret;
4037 if (mm->iArrange & ARW_STARTRIGHT)
4038 ret.x = parent->right - mm->iHorzGap - width;
4039 else
4040 ret.x = parent->left + mm->iHorzGap;
4041 if (mm->iArrange & ARW_STARTTOP)
4042 ret.y = parent->top + mm->iVertGap;
4043 else
4044 ret.y = parent->bottom - mm->iVertGap - height;
4046 return ret;
4049 static void get_next_minimized_child_pos( const RECT *parent, const MINIMIZEDMETRICS *mm,
4050 int width, int height, POINT *pos )
4052 BOOL next;
4054 if (mm->iArrange & ARW_UP) /* == ARW_DOWN */
4056 if (mm->iArrange & ARW_STARTTOP)
4058 pos->y += height + mm->iVertGap;
4059 if ((next = pos->y + height > parent->bottom))
4060 pos->y = parent->top + mm->iVertGap;
4062 else
4064 pos->y -= height + mm->iVertGap;
4065 if ((next = pos->y < parent->top))
4066 pos->y = parent->bottom - mm->iVertGap - height;
4069 if (next)
4071 if (mm->iArrange & ARW_STARTRIGHT)
4072 pos->x -= width + mm->iHorzGap;
4073 else
4074 pos->x += width + mm->iHorzGap;
4077 else
4079 if (mm->iArrange & ARW_STARTRIGHT)
4081 pos->x -= width + mm->iHorzGap;
4082 if ((next = pos->x < parent->left))
4083 pos->x = parent->right - mm->iHorzGap - width;
4085 else
4087 pos->x += width + mm->iHorzGap;
4088 if ((next = pos->x + width > parent->right))
4089 pos->x = parent->left + mm->iHorzGap;
4092 if (next)
4094 if (mm->iArrange & ARW_STARTTOP)
4095 pos->y += height + mm->iVertGap;
4096 else
4097 pos->y -= height + mm->iVertGap;
4102 static POINT get_minimized_pos( HWND hwnd, POINT pt )
4104 RECT rect, parent_rect;
4105 HWND parent, child;
4106 HRGN hrgn, tmp;
4107 MINIMIZEDMETRICS metrics;
4108 int width, height;
4110 parent = NtUserGetAncestor( hwnd, GA_PARENT );
4111 if (parent == get_desktop_window())
4113 MONITORINFO mon_info;
4114 HMONITOR monitor = monitor_from_window( hwnd, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
4116 mon_info.cbSize = sizeof( mon_info );
4117 get_monitor_info( monitor, &mon_info, get_thread_dpi() );
4118 parent_rect = mon_info.rcWork;
4120 else get_client_rect( parent, &parent_rect, get_thread_dpi() );
4122 if (pt.x >= parent_rect.left && (pt.x + get_system_metrics( SM_CXMINIMIZED ) < parent_rect.right) &&
4123 pt.y >= parent_rect.top && (pt.y + get_system_metrics( SM_CYMINIMIZED ) < parent_rect.bottom))
4124 return pt; /* The icon already has a suitable position */
4126 width = get_system_metrics( SM_CXMINIMIZED );
4127 height = get_system_metrics( SM_CYMINIMIZED );
4129 metrics.cbSize = sizeof(metrics);
4130 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(metrics), &metrics, 0 );
4132 /* Check if another icon already occupies this spot */
4133 /* FIXME: this is completely inefficient */
4135 hrgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
4136 tmp = NtGdiCreateRectRgn( 0, 0, 0, 0 );
4137 for (child = get_window_relative( parent, GW_CHILD );
4138 child;
4139 child = get_window_relative( child, GW_HWNDNEXT ))
4141 if (child == hwnd) continue;
4142 if ((get_window_long( child, GWL_STYLE ) & (WS_VISIBLE|WS_MINIMIZE)) != (WS_VISIBLE|WS_MINIMIZE))
4143 continue;
4144 if (get_window_rects( child, COORDS_PARENT, &rect, NULL, get_thread_dpi() ))
4146 NtGdiSetRectRgn( tmp, rect.left, rect.top, rect.right, rect.bottom );
4147 NtGdiCombineRgn( hrgn, hrgn, tmp, RGN_OR );
4150 NtGdiDeleteObjectApp( tmp );
4152 pt = get_first_minimized_child_pos( &parent_rect, &metrics, width, height );
4153 for (;;)
4155 SetRect( &rect, pt.x, pt.y, pt.x + width, pt.y + height );
4156 if (!NtGdiRectInRegion( hrgn, &rect ))
4157 break;
4159 get_next_minimized_child_pos( &parent_rect, &metrics, width, height, &pt );
4162 NtGdiDeleteObjectApp( hrgn );
4163 return pt;
4166 /***********************************************************************
4167 * window_min_maximize
4169 static UINT window_min_maximize( HWND hwnd, UINT cmd, RECT *rect )
4171 UINT swp_flags = 0;
4172 LONG old_style;
4173 MINMAXINFO minmax;
4174 WINDOWPLACEMENT wpl;
4176 TRACE( "%p %u\n", hwnd, cmd );
4178 wpl.length = sizeof(wpl);
4179 NtUserGetWindowPlacement( hwnd, &wpl );
4181 if (call_hooks( WH_CBT, HCBT_MINMAX, (WPARAM)hwnd, cmd, 0 ))
4182 return SWP_NOSIZE | SWP_NOMOVE;
4184 if (is_iconic( hwnd ))
4186 switch (cmd)
4188 case SW_SHOWMINNOACTIVE:
4189 case SW_SHOWMINIMIZED:
4190 case SW_FORCEMINIMIZE:
4191 case SW_MINIMIZE:
4192 wpl.ptMinPosition = get_minimized_pos( hwnd, wpl.ptMinPosition );
4194 SetRect( rect, wpl.ptMinPosition.x, wpl.ptMinPosition.y,
4195 wpl.ptMinPosition.x + get_system_metrics( SM_CXMINIMIZED ),
4196 wpl.ptMinPosition.y + get_system_metrics( SM_CYMINIMIZED ));
4197 return SWP_NOSIZE | SWP_NOMOVE;
4199 if (!send_message( hwnd, WM_QUERYOPEN, 0, 0 )) return SWP_NOSIZE | SWP_NOMOVE;
4200 swp_flags |= SWP_NOCOPYBITS;
4203 switch( cmd )
4205 case SW_SHOWMINNOACTIVE:
4206 case SW_SHOWMINIMIZED:
4207 case SW_FORCEMINIMIZE:
4208 case SW_MINIMIZE:
4209 if (is_zoomed( hwnd )) win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
4210 else win_set_flags( hwnd, 0, WIN_RESTORE_MAX );
4212 if (get_focus() == hwnd)
4214 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD)
4215 NtUserSetFocus( NtUserGetAncestor( hwnd, GA_PARENT ));
4216 else
4217 NtUserSetFocus( 0 );
4220 old_style = set_window_style( hwnd, WS_MINIMIZE, WS_MAXIMIZE );
4222 wpl.ptMinPosition = get_minimized_pos( hwnd, wpl.ptMinPosition );
4224 if (!(old_style & WS_MINIMIZE)) swp_flags |= SWP_STATECHANGED;
4225 SetRect( rect, wpl.ptMinPosition.x, wpl.ptMinPosition.y,
4226 wpl.ptMinPosition.x + get_system_metrics(SM_CXMINIMIZED),
4227 wpl.ptMinPosition.y + get_system_metrics(SM_CYMINIMIZED) );
4228 swp_flags |= SWP_NOCOPYBITS;
4229 break;
4231 case SW_MAXIMIZE:
4232 old_style = get_window_long( hwnd, GWL_STYLE );
4233 if ((old_style & WS_MAXIMIZE) && (old_style & WS_VISIBLE)) return SWP_NOSIZE | SWP_NOMOVE;
4235 minmax = get_min_max_info( hwnd );
4237 old_style = set_window_style( hwnd, WS_MAXIMIZE, WS_MINIMIZE );
4238 if (old_style & WS_MINIMIZE)
4239 win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
4241 if (!(old_style & WS_MAXIMIZE)) swp_flags |= SWP_STATECHANGED;
4242 SetRect( rect, minmax.ptMaxPosition.x, minmax.ptMaxPosition.y,
4243 minmax.ptMaxPosition.x + minmax.ptMaxSize.x,
4244 minmax.ptMaxPosition.y + minmax.ptMaxSize.y );
4245 break;
4247 case SW_SHOWNOACTIVATE:
4248 win_set_flags( hwnd, 0, WIN_RESTORE_MAX );
4249 /* fall through */
4250 case SW_SHOWNORMAL:
4251 case SW_RESTORE:
4252 case SW_SHOWDEFAULT: /* FIXME: should have its own handler */
4253 old_style = set_window_style( hwnd, 0, WS_MINIMIZE | WS_MAXIMIZE );
4254 if (old_style & WS_MINIMIZE)
4256 if (win_get_flags( hwnd ) & WIN_RESTORE_MAX)
4258 /* Restore to maximized position */
4259 minmax = get_min_max_info( hwnd );
4260 set_window_style( hwnd, WS_MAXIMIZE, 0 );
4261 swp_flags |= SWP_STATECHANGED;
4262 SetRect( rect, minmax.ptMaxPosition.x, minmax.ptMaxPosition.y,
4263 minmax.ptMaxPosition.x + minmax.ptMaxSize.x,
4264 minmax.ptMaxPosition.y + minmax.ptMaxSize.y );
4265 break;
4268 else if (!(old_style & WS_MAXIMIZE)) break;
4270 swp_flags |= SWP_STATECHANGED;
4272 /* Restore to normal position */
4274 *rect = wpl.rcNormalPosition;
4275 break;
4278 return swp_flags;
4281 /* see ArrangeIconicWindows */
4282 static UINT arrange_iconic_windows( HWND parent )
4284 int width, height, count = 0;
4285 MINIMIZEDMETRICS metrics;
4286 RECT parent_rect;
4287 HWND child;
4288 POINT pt;
4290 metrics.cbSize = sizeof(metrics);
4291 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(metrics), &metrics, 0 );
4292 width = get_system_metrics( SM_CXMINIMIZED );
4293 height = get_system_metrics( SM_CYMINIMIZED );
4295 if (parent == get_desktop_window())
4297 MONITORINFO mon_info;
4298 HMONITOR monitor = monitor_from_window( 0, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
4300 mon_info.cbSize = sizeof( mon_info );
4301 get_monitor_info( monitor, &mon_info, get_thread_dpi() );
4302 parent_rect = mon_info.rcWork;
4304 else get_client_rect( parent, &parent_rect, get_thread_dpi() );
4306 pt = get_first_minimized_child_pos( &parent_rect, &metrics, width, height );
4308 child = get_window_relative( parent, GW_CHILD );
4309 while (child)
4311 if (is_iconic( child ))
4313 NtUserSetWindowPos( child, 0, pt.x, pt.y, 0, 0,
4314 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
4315 get_next_minimized_child_pos( &parent_rect, &metrics, width, height, &pt );
4316 count++;
4318 child = get_window_relative( child, GW_HWNDNEXT );
4320 return count;
4323 /*******************************************************************
4324 * update_window_state
4326 * Trigger an update of the window's driver state and surface.
4328 void update_window_state( HWND hwnd )
4330 static const UINT swp_flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE |
4331 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW;
4332 UINT context;
4333 RECT window_rect, client_rect, valid_rects[2], visible_rect, surface_rect;
4334 struct window_surface *surface;
4336 if (!is_current_thread_window( hwnd ))
4338 NtUserPostMessage( hwnd, WM_WINE_UPDATEWINDOWSTATE, 0, 0 );
4339 return;
4342 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
4343 get_window_rects( hwnd, COORDS_PARENT, &window_rect, &client_rect, get_thread_dpi() );
4344 valid_rects[0] = valid_rects[1] = client_rect;
4346 surface = create_window_surface( hwnd, swp_flags, FALSE, &window_rect, &client_rect, &visible_rect, &surface_rect );
4347 apply_window_pos( hwnd, 0, swp_flags, surface, &window_rect, &client_rect, &visible_rect, valid_rects );
4348 if (surface) window_surface_release( surface );
4350 set_thread_dpi_awareness_context( context );
4353 /***********************************************************************
4354 * show_window
4356 * Implementation of ShowWindow and ShowWindowAsync.
4358 static BOOL show_window( HWND hwnd, INT cmd )
4360 WND *win;
4361 HWND parent;
4362 LONG style = get_window_long( hwnd, GWL_STYLE ), new_style;
4363 BOOL was_visible = (style & WS_VISIBLE) != 0;
4364 BOOL show_flag = TRUE;
4365 RECT newPos = {0, 0, 0, 0};
4366 UINT new_swp, swp = 0, context;
4368 TRACE( "hwnd=%p, cmd=%d, was_visible %d\n", hwnd, cmd, was_visible );
4370 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
4372 switch(cmd)
4374 case SW_HIDE:
4375 if (!was_visible) goto done;
4376 show_flag = FALSE;
4377 swp |= SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE;
4378 if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4379 break;
4381 case SW_SHOWMINNOACTIVE:
4382 case SW_MINIMIZE:
4383 case SW_FORCEMINIMIZE: /* FIXME: Does not work if thread is hung. */
4384 swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4385 /* fall through */
4386 case SW_SHOWMINIMIZED:
4387 swp |= SWP_SHOWWINDOW | SWP_FRAMECHANGED;
4388 swp |= window_min_maximize( hwnd, cmd, &newPos );
4389 if ((style & WS_MINIMIZE) && was_visible) goto done;
4390 break;
4392 case SW_SHOWMAXIMIZED: /* same as SW_MAXIMIZE */
4393 if (!was_visible) swp |= SWP_SHOWWINDOW;
4394 swp |= SWP_FRAMECHANGED;
4395 swp |= window_min_maximize( hwnd, SW_MAXIMIZE, &newPos );
4396 if ((style & WS_MAXIMIZE) && was_visible) goto done;
4397 break;
4399 case SW_SHOWNA:
4400 swp |= SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
4401 if (style & WS_CHILD) swp |= SWP_NOZORDER;
4402 break;
4404 case SW_SHOW:
4405 if (was_visible) goto done;
4406 swp |= SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
4407 if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4408 break;
4410 case SW_SHOWNOACTIVATE:
4411 swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4412 /* fall through */
4413 case SW_RESTORE:
4414 /* fall through */
4415 case SW_SHOWNORMAL: /* same as SW_NORMAL: */
4416 case SW_SHOWDEFAULT: /* FIXME: should have its own handler */
4417 if (!was_visible) swp |= SWP_SHOWWINDOW;
4418 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
4420 swp |= SWP_FRAMECHANGED;
4421 swp |= window_min_maximize( hwnd, cmd, &newPos );
4423 else
4425 if (was_visible) goto done;
4426 swp |= SWP_NOSIZE | SWP_NOMOVE;
4428 if (style & WS_CHILD && !(swp & SWP_STATECHANGED)) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4429 break;
4431 default:
4432 goto done;
4435 if ((show_flag != was_visible || cmd == SW_SHOWNA) && cmd != SW_SHOWMAXIMIZED && !(swp & SWP_STATECHANGED))
4437 send_message( hwnd, WM_SHOWWINDOW, show_flag, 0 );
4438 if (!is_window( hwnd )) goto done;
4441 if (IsRectEmpty( &newPos )) new_swp = swp;
4442 else if ((new_swp = user_driver->pShowWindow( hwnd, cmd, &newPos, swp )) == ~0)
4444 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) new_swp = swp;
4445 else if (is_iconic( hwnd ) && (newPos.left != -32000 || newPos.top != -32000))
4447 OffsetRect( &newPos, -32000 - newPos.left, -32000 - newPos.top );
4448 new_swp = swp & ~(SWP_NOMOVE | SWP_NOCLIENTMOVE);
4450 else new_swp = swp;
4452 swp = new_swp;
4454 parent = NtUserGetAncestor( hwnd, GA_PARENT );
4455 if (parent && !is_window_visible( parent ) && !(swp & SWP_STATECHANGED))
4457 /* if parent is not visible simply toggle WS_VISIBLE and return */
4458 if (show_flag) set_window_style( hwnd, WS_VISIBLE, 0 );
4459 else set_window_style( hwnd, 0, WS_VISIBLE );
4461 else
4462 NtUserSetWindowPos( hwnd, HWND_TOP, newPos.left, newPos.top,
4463 newPos.right - newPos.left, newPos.bottom - newPos.top, swp );
4465 new_style = get_window_long( hwnd, GWL_STYLE );
4466 if (((style ^ new_style) & WS_MINIMIZE) != 0)
4468 if ((new_style & WS_MINIMIZE) != 0)
4469 NtUserNotifyWinEvent( EVENT_SYSTEM_MINIMIZESTART, hwnd, OBJID_WINDOW, 0 );
4470 else
4471 NtUserNotifyWinEvent( EVENT_SYSTEM_MINIMIZEEND, hwnd, OBJID_WINDOW, 0 );
4474 if (cmd == SW_HIDE)
4476 HWND hFocus;
4478 /* FIXME: This will cause the window to be activated irrespective
4479 * of whether it is owned by the same thread. Has to be done
4480 * asynchronously.
4483 if (hwnd == get_active_window()) activate_other_window( hwnd );
4485 /* Revert focus to parent */
4486 hFocus = get_focus();
4487 if (hwnd == hFocus)
4489 HWND parent = NtUserGetAncestor(hwnd, GA_PARENT);
4490 if (parent == get_desktop_window()) parent = 0;
4491 NtUserSetFocus(parent);
4493 goto done;
4496 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) goto done;
4498 if (win->flags & WIN_NEED_SIZE)
4500 /* should happen only in CreateWindowEx() */
4501 int wParam = SIZE_RESTORED;
4502 RECT client;
4503 LPARAM lparam;
4505 get_window_rects( hwnd, COORDS_PARENT, NULL, &client, get_thread_dpi() );
4506 lparam = MAKELONG( client.right - client.left, client.bottom - client.top );
4507 win->flags &= ~WIN_NEED_SIZE;
4508 if (win->dwStyle & WS_MAXIMIZE) wParam = SIZE_MAXIMIZED;
4509 else if (win->dwStyle & WS_MINIMIZE)
4511 wParam = SIZE_MINIMIZED;
4512 lparam = 0;
4514 release_win_ptr( win );
4516 send_message( hwnd, WM_SIZE, wParam, lparam );
4517 send_message( hwnd, WM_MOVE, 0, MAKELONG( client.left, client.top ));
4519 else release_win_ptr( win );
4521 /* if previous state was minimized Windows sets focus to the window */
4522 if (style & WS_MINIMIZE)
4524 NtUserSetFocus( hwnd );
4525 /* Send a WM_ACTIVATE message for a top level window, even if the window is already active */
4526 if (NtUserGetAncestor( hwnd, GA_ROOT ) == hwnd && !(swp & SWP_NOACTIVATE))
4527 send_message( hwnd, WM_ACTIVATE, WA_ACTIVE, 0 );
4530 done:
4531 set_thread_dpi_awareness_context( context );
4532 return was_visible;
4535 /***********************************************************************
4536 * NtUserShowWindowAsync (win32u.@)
4538 * doesn't wait; returns immediately.
4539 * used by threads to toggle windows in other (possibly hanging) threads
4541 BOOL WINAPI NtUserShowWindowAsync( HWND hwnd, INT cmd )
4543 HWND full_handle;
4545 if (is_broadcast(hwnd))
4547 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4548 return FALSE;
4551 if ((full_handle = is_current_thread_window( hwnd )))
4552 return show_window( full_handle, cmd );
4554 return NtUserMessageCall( hwnd, WM_WINE_SHOWWINDOW, cmd, 0, 0,
4555 NtUserSendNotifyMessage, FALSE );
4558 /***********************************************************************
4559 * NtUserShowWindow (win32u.@)
4561 BOOL WINAPI NtUserShowWindow( HWND hwnd, INT cmd )
4563 HWND full_handle;
4565 if (is_broadcast(hwnd))
4567 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4568 return FALSE;
4570 if ((full_handle = is_current_thread_window( hwnd )))
4571 return show_window( full_handle, cmd );
4573 if ((cmd == SW_HIDE) && !(get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE))
4574 return FALSE;
4576 if ((cmd == SW_SHOW) && (get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE))
4577 return TRUE;
4579 return send_message( hwnd, WM_WINE_SHOWWINDOW, cmd, 0 );
4582 /* see ShowOwnedPopups */
4583 BOOL show_owned_popups( HWND owner, BOOL show )
4585 int count = 0;
4586 HWND *win_array = list_window_children( 0, get_desktop_window(), NULL, 0 );
4588 if (!win_array) return TRUE;
4590 while (win_array[count]) count++;
4591 while (--count >= 0)
4593 if (get_window_relative( win_array[count], GW_OWNER ) != owner) continue;
4594 if (show)
4596 if (win_get_flags( win_array[count] ) & WIN_NEEDS_SHOW_OWNEDPOPUP)
4597 /* In Windows, ShowOwnedPopups(TRUE) generates
4598 * WM_SHOWWINDOW messages with SW_PARENTOPENING,
4599 * regardless of the state of the owner
4601 send_message( win_array[count], WM_SHOWWINDOW, SW_SHOWNORMAL, SW_PARENTOPENING );
4603 else
4605 if (get_window_long( win_array[count], GWL_STYLE ) & WS_VISIBLE)
4606 /* In Windows, ShowOwnedPopups(FALSE) generates
4607 * WM_SHOWWINDOW messages with SW_PARENTCLOSING,
4608 * regardless of the state of the owner
4610 send_message( win_array[count], WM_SHOWWINDOW, SW_HIDE, SW_PARENTCLOSING );
4614 free( win_array );
4615 return TRUE;
4618 /*******************************************************************
4619 * NtUserFlashWindowEx (win32u.@)
4621 BOOL WINAPI NtUserFlashWindowEx( FLASHWINFO *info )
4623 WND *win;
4625 TRACE( "%p\n", info );
4627 if (!info)
4629 RtlSetLastWin32Error( ERROR_NOACCESS );
4630 return FALSE;
4633 if (!info->hwnd || info->cbSize != sizeof(FLASHWINFO) || !is_window( info->hwnd ))
4635 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4636 return FALSE;
4638 FIXME( "%p - semi-stub\n", info );
4640 if (is_iconic( info->hwnd ))
4642 NtUserRedrawWindow( info->hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_FRAME );
4644 win = get_win_ptr( info->hwnd );
4645 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
4646 if (info->dwFlags & FLASHW_CAPTION && !(win->flags & WIN_NCACTIVATED))
4648 win->flags |= WIN_NCACTIVATED;
4650 else if (!info->dwFlags)
4652 win->flags &= ~WIN_NCACTIVATED;
4654 release_win_ptr( win );
4655 user_driver->pFlashWindowEx( info );
4656 return TRUE;
4658 else
4660 WPARAM wparam;
4661 HWND hwnd = info->hwnd;
4663 win = get_win_ptr( hwnd );
4664 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
4665 hwnd = win->obj.handle; /* make it a full handle */
4667 if (info->dwFlags) wparam = !(win->flags & WIN_NCACTIVATED);
4668 else wparam = (hwnd == NtUserGetForegroundWindow());
4670 release_win_ptr( win );
4672 if (!info->dwFlags || info->dwFlags & FLASHW_CAPTION)
4673 send_message( hwnd, WM_NCACTIVATE, wparam, 0 );
4675 user_driver->pFlashWindowEx( info );
4676 return wparam;
4680 /* see GetWindowContextHelpId */
4681 DWORD get_window_context_help_id( HWND hwnd )
4683 DWORD retval;
4684 WND *win = get_win_ptr( hwnd );
4685 if (!win || win == WND_DESKTOP) return 0;
4686 if (win == WND_OTHER_PROCESS)
4688 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4689 return 0;
4691 retval = win->helpContext;
4692 release_win_ptr( win );
4693 return retval;
4696 /* see SetWindowContextHelpId */
4697 static BOOL set_window_context_help_id( HWND hwnd, DWORD id )
4699 WND *win = get_win_ptr( hwnd );
4700 if (!win || win == WND_DESKTOP) return FALSE;
4701 if (win == WND_OTHER_PROCESS)
4703 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4704 return FALSE;
4706 win->helpContext = id;
4707 release_win_ptr( win );
4708 return TRUE;
4711 /***********************************************************************
4712 * NtUserInternalGetWindowIcon (win32u.@)
4714 HICON WINAPI NtUserInternalGetWindowIcon( HWND hwnd, UINT type )
4716 WND *win = get_win_ptr( hwnd );
4717 HICON ret;
4719 TRACE( "hwnd %p, type %#x\n", hwnd, type );
4721 if (!win)
4723 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
4724 return 0;
4726 if (win == WND_OTHER_PROCESS || win == WND_DESKTOP)
4728 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4729 return 0;
4732 switch (type)
4734 case ICON_BIG:
4735 ret = win->hIcon;
4736 if (!ret) ret = (HICON)get_class_long_ptr( hwnd, GCLP_HICON, FALSE );
4737 break;
4739 case ICON_SMALL:
4740 case ICON_SMALL2:
4741 ret = win->hIconSmall ? win->hIconSmall : win->hIconSmall2;
4742 if (!ret) ret = (HICON)get_class_long_ptr( hwnd, GCLP_HICONSM, FALSE );
4743 if (!ret) ret = (HICON)get_class_long_ptr( hwnd, GCLP_HICON, FALSE );
4744 break;
4746 default:
4747 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4748 release_win_ptr( win );
4749 return 0;
4751 release_win_ptr( win );
4753 if (!ret) ret = LoadImageW( 0, (const WCHAR *)IDI_APPLICATION, IMAGE_ICON,
4754 0, 0, LR_SHARED | LR_DEFAULTSIZE );
4756 return CopyImage( ret, IMAGE_ICON, 0, 0, 0 );
4759 /***********************************************************************
4760 * send_destroy_message
4762 static void send_destroy_message( HWND hwnd, BOOL winevent )
4764 GUITHREADINFO info;
4766 info.cbSize = sizeof(info);
4767 if (NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ))
4769 if (hwnd == info.hwndCaret) destroy_caret();
4770 if (hwnd == info.hwndActive) activate_other_window( hwnd );
4773 if (hwnd == NtUserGetClipboardOwner()) release_clipboard_owner( hwnd );
4775 if (winevent)
4776 NtUserNotifyWinEvent( EVENT_OBJECT_DESTROY, hwnd, OBJID_WINDOW, 0 );
4778 send_message( hwnd, WM_DESTROY, 0, 0);
4781 * This WM_DESTROY message can trigger re-entrant calls to DestroyWindow
4782 * make sure that the window still exists when we come back.
4784 if (is_window(hwnd))
4786 HWND *children;
4787 int i;
4789 if (!(children = list_window_children( 0, hwnd, NULL, 0 ))) return;
4791 for (i = 0; children[i]; i++)
4793 if (is_window( children[i] )) send_destroy_message( children[i], FALSE );
4795 free( children );
4797 else
4798 WARN( "\tdestroyed itself while in WM_DESTROY!\n" );
4801 /***********************************************************************
4802 * free_window_handle
4804 * Free a window handle.
4806 static void free_window_handle( HWND hwnd )
4808 WND *win;
4810 TRACE( "\n" );
4812 if ((win = get_user_handle_ptr( hwnd, NTUSER_OBJ_WINDOW )) && win != OBJ_OTHER_PROCESS)
4814 SERVER_START_REQ( destroy_window )
4816 req->handle = wine_server_user_handle( hwnd );
4817 wine_server_call( req );
4818 set_user_handle_ptr( hwnd, NULL );
4820 SERVER_END_REQ;
4821 user_unlock();
4822 free( win->pScroll );
4823 free( win->text );
4824 free( win );
4828 /***********************************************************************
4829 * destroy_window
4831 LRESULT destroy_window( HWND hwnd )
4833 struct list vulkan_surfaces = LIST_INIT(vulkan_surfaces);
4834 struct window_surface *surface;
4835 HMENU menu = 0, sys_menu;
4836 WND *win;
4837 HWND *children;
4839 TRACE( "%p\n", hwnd );
4841 unregister_imm_window( hwnd );
4843 /* free child windows */
4844 if ((children = list_window_children( 0, hwnd, NULL, 0 )))
4846 int i;
4847 for (i = 0; children[i]; i++)
4849 if (is_current_thread_window( children[i] ))
4850 destroy_window( children[i] );
4851 else
4852 NtUserMessageCall( children[i], WM_WINE_DESTROYWINDOW, 0, 0,
4853 0, NtUserSendNotifyMessage, FALSE );
4855 free( children );
4858 /* Unlink now so we won't bother with the children later on */
4859 SERVER_START_REQ( set_parent )
4861 req->handle = wine_server_user_handle( hwnd );
4862 req->parent = 0;
4863 wine_server_call( req );
4865 SERVER_END_REQ;
4867 send_message( hwnd, WM_NCDESTROY, 0, 0 );
4869 /* FIXME: do we need to fake QS_MOUSEMOVE wakebit? */
4871 /* free resources associated with the window */
4873 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
4874 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
4875 menu = (HMENU)win->wIDmenu;
4876 sys_menu = win->hSysMenu;
4877 free_dce( win->dce, hwnd );
4878 win->dce = NULL;
4879 NtUserDestroyCursor( win->hIconSmall2, 0 );
4880 list_move_tail( &vulkan_surfaces, &win->vulkan_surfaces );
4881 surface = win->surface;
4882 win->surface = NULL;
4883 release_win_ptr( win );
4885 NtUserDestroyMenu( menu );
4886 NtUserDestroyMenu( sys_menu );
4887 if (surface)
4889 register_window_surface( surface, NULL );
4890 window_surface_release( surface );
4893 vulkan_detach_surfaces( &vulkan_surfaces );
4894 user_driver->pDestroyWindow( hwnd );
4896 free_window_handle( hwnd );
4897 return 0;
4900 /***********************************************************************
4901 * NtUserDestroyWindow (win32u.@)
4903 static BOOL user_destroy_window( HWND hwnd, BOOL winevent )
4905 BOOL is_child;
4907 if (!(hwnd = is_current_thread_window( hwnd )) || is_desktop_window( hwnd ))
4909 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
4910 return FALSE;
4913 TRACE( "(%p)\n", hwnd );
4915 if (call_hooks( WH_CBT, HCBT_DESTROYWND, (WPARAM)hwnd, 0, 0 )) return FALSE;
4917 if (is_menu_active() == hwnd) NtUserEndMenu();
4919 is_child = (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) != 0;
4921 if (is_child)
4923 if (!is_exiting_thread( GetCurrentThreadId() ))
4924 send_parent_notify( hwnd, WM_DESTROY );
4926 else if (!get_window_relative( hwnd, GW_OWNER ))
4928 call_hooks( WH_SHELL, HSHELL_WINDOWDESTROYED, (WPARAM)hwnd, 0, 0 );
4929 /* FIXME: clean up palette - see "Internals" p.352 */
4932 if (!is_window( hwnd )) return TRUE;
4934 /* Hide the window */
4935 if (get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE)
4937 /* Only child windows receive WM_SHOWWINDOW in DestroyWindow() */
4938 if (is_child)
4939 NtUserShowWindow( hwnd, SW_HIDE );
4940 else
4941 NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE |
4942 SWP_NOZORDER | SWP_NOACTIVATE | SWP_HIDEWINDOW );
4945 if (!is_window( hwnd )) return TRUE;
4947 /* Recursively destroy child windows */
4948 if (!is_child)
4950 for (;;)
4952 BOOL got_one = FALSE;
4953 HWND *children;
4954 unsigned int i;
4956 if (!(children = list_window_children( 0, get_desktop_window(), NULL, 0 ))) break;
4958 for (i = 0; children[i]; i++)
4960 if (get_window_relative( children[i], GW_OWNER ) != hwnd) continue;
4961 if (is_current_thread_window( children[i] ))
4963 user_destroy_window( children[i], FALSE );
4964 got_one = TRUE;
4965 continue;
4967 set_window_owner( children[i], 0 );
4969 free( children );
4970 if (!got_one) break;
4974 send_destroy_message( hwnd, winevent );
4975 if (!is_window( hwnd )) return TRUE;
4977 destroy_window( hwnd );
4978 return TRUE;
4981 BOOL WINAPI NtUserDestroyWindow( HWND hwnd )
4983 return user_destroy_window( hwnd, TRUE );
4986 /*****************************************************************************
4987 * destroy_thread_windows
4989 * Destroy all window owned by the current thread.
4991 void destroy_thread_windows(void)
4993 WND *win, *free_list = NULL;
4994 HANDLE handle = 0;
4996 user_lock();
4997 while ((win = next_process_user_handle_ptr( &handle, NTUSER_OBJ_WINDOW )))
4999 if (win->tid != GetCurrentThreadId()) continue;
5000 free_dce( win->dce, win->obj.handle );
5001 set_user_handle_ptr( handle, NULL );
5002 win->obj.handle = free_list;
5003 free_list = win;
5005 if (free_list)
5007 SERVER_START_REQ( destroy_window )
5009 req->handle = 0; /* destroy all thread windows */
5010 wine_server_call( req );
5012 SERVER_END_REQ;
5014 user_unlock();
5016 while ((win = free_list))
5018 free_list = win->obj.handle;
5019 TRACE( "destroying %p\n", win );
5021 vulkan_detach_surfaces( &win->vulkan_surfaces );
5023 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD && win->wIDmenu)
5024 NtUserDestroyMenu( UlongToHandle(win->wIDmenu) );
5025 if (win->hSysMenu) NtUserDestroyMenu( win->hSysMenu );
5026 if (win->surface)
5028 register_window_surface( win->surface, NULL );
5029 window_surface_release( win->surface );
5031 free( win->pScroll );
5032 free( win->text );
5033 free( win );
5037 /***********************************************************************
5038 * create_window_handle
5040 * Create a window handle with the server.
5042 static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name,
5043 HINSTANCE instance, BOOL ansi,
5044 DWORD style, DWORD ex_style )
5046 UINT dpi_context = get_thread_dpi_awareness_context();
5047 HWND handle = 0, full_parent = 0, full_owner = 0;
5048 struct tagCLASS *class = NULL;
5049 int extra_bytes = 0;
5050 WND *win;
5052 SERVER_START_REQ( create_window )
5054 req->parent = wine_server_user_handle( parent );
5055 req->owner = wine_server_user_handle( owner );
5056 req->instance = wine_server_client_ptr( instance );
5057 req->dpi_context = dpi_context;
5058 req->style = style;
5059 req->ex_style = ex_style;
5060 if (!(req->atom = get_int_atom_value( name )) && name->Length)
5061 wine_server_add_data( req, name->Buffer, name->Length );
5062 if (!wine_server_call_err( req ))
5064 handle = wine_server_ptr_handle( reply->handle );
5065 full_parent = wine_server_ptr_handle( reply->parent );
5066 full_owner = wine_server_ptr_handle( reply->owner );
5067 extra_bytes = reply->extra;
5068 dpi_context = reply->dpi_context;
5069 class = wine_server_get_ptr( reply->class_ptr );
5072 SERVER_END_REQ;
5074 if (!handle)
5076 WARN( "error %d creating window\n", (int)RtlGetLastWin32Error() );
5077 return NULL;
5080 if (!(win = calloc( 1, FIELD_OFFSET(WND, wExtra) + extra_bytes )))
5082 SERVER_START_REQ( destroy_window )
5084 req->handle = wine_server_user_handle( handle );
5085 wine_server_call( req );
5087 SERVER_END_REQ;
5088 RtlSetLastWin32Error( ERROR_NOT_ENOUGH_MEMORY );
5089 return NULL;
5092 if (!parent) /* if parent is 0 we don't have a desktop window yet */
5094 struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
5096 if (name->Buffer == (const WCHAR *)DESKTOP_CLASS_ATOM)
5098 if (!thread_info->top_window) thread_info->top_window = HandleToUlong( full_parent ? full_parent : handle );
5099 else assert( full_parent == UlongToHandle( thread_info->top_window ));
5100 if (!thread_info->top_window) ERR_(win)( "failed to create desktop window\n" );
5101 else user_driver->pSetDesktopWindow( UlongToHandle( thread_info->top_window ));
5102 register_builtin_classes();
5104 else /* HWND_MESSAGE parent */
5106 if (!thread_info->msg_window && !full_parent)
5107 thread_info->msg_window = HandleToUlong( handle );
5111 user_lock();
5113 win->obj.handle = handle;
5114 win->obj.type = NTUSER_OBJ_WINDOW;
5115 win->parent = full_parent;
5116 win->owner = full_owner;
5117 win->class = class;
5118 win->winproc = get_class_winproc( class );
5119 win->cbWndExtra = extra_bytes;
5120 win->dpi_context = dpi_context;
5121 list_init( &win->vulkan_surfaces );
5122 set_user_handle_ptr( handle, &win->obj );
5123 if (is_winproc_unicode( win->winproc, !ansi )) win->flags |= WIN_ISUNICODE;
5124 return win;
5127 static BOOL is_default_coord( int x )
5129 return x == CW_USEDEFAULT || x == 0x8000;
5132 /***********************************************************************
5133 * fix_cs_coordinates
5135 * Fix the coordinates and return default show mode in sw.
5137 static void fix_cs_coordinates( CREATESTRUCTW *cs, INT *sw )
5139 if (cs->style & (WS_CHILD | WS_POPUP))
5141 if (is_default_coord(cs->x)) cs->x = cs->y = 0;
5142 if (is_default_coord(cs->cx)) cs->cx = cs->cy = 0;
5144 else /* overlapped window */
5146 RTL_USER_PROCESS_PARAMETERS *params = NtCurrentTeb()->Peb->ProcessParameters;
5147 HMONITOR monitor;
5148 MONITORINFO mon_info;
5150 if (!is_default_coord( cs->x ) && !is_default_coord( cs->cx ) && !is_default_coord( cs->cy ))
5151 return;
5153 monitor = monitor_from_window( cs->hwndParent, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
5154 mon_info.cbSize = sizeof(mon_info);
5155 get_monitor_info( monitor, &mon_info, get_thread_dpi() );
5157 if (is_default_coord( cs->x ))
5159 if (!is_default_coord( cs->y )) *sw = cs->y;
5160 cs->x = (params->dwFlags & STARTF_USEPOSITION) ? params->dwX : mon_info.rcWork.left;
5161 cs->y = (params->dwFlags & STARTF_USEPOSITION) ? params->dwY : mon_info.rcWork.top;
5164 if (is_default_coord( cs->cx ))
5166 if (params->dwFlags & STARTF_USESIZE)
5168 cs->cx = params->dwXSize;
5169 cs->cy = params->dwYSize;
5171 else
5173 cs->cx = (mon_info.rcWork.right - mon_info.rcWork.left) * 3 / 4 - cs->x;
5174 cs->cy = (mon_info.rcWork.bottom - mon_info.rcWork.top) * 3 / 4 - cs->y;
5177 /* neither x nor cx are default. Check the y values.
5178 * In the trace we see Outlook and Outlook Express using
5179 * cy set to CW_USEDEFAULT when opening the address book.
5181 else if (is_default_coord( cs->cy ))
5183 FIXME( "Strange use of CW_USEDEFAULT in cy\n" );
5184 cs->cy = (mon_info.rcWork.bottom - mon_info.rcWork.top) * 3 / 4 - cs->y;
5189 /***********************************************************************
5190 * map_dpi_create_struct
5192 static void map_dpi_create_struct( CREATESTRUCTW *cs, UINT dpi_from, UINT dpi_to )
5194 if (!dpi_from && !dpi_to) return;
5195 if (!dpi_from || !dpi_to)
5197 POINT pt = { cs->x, cs->y };
5198 UINT mon_dpi = get_monitor_dpi( monitor_from_point( pt, MONITOR_DEFAULTTONEAREST, dpi_from ));
5199 if (!dpi_from) dpi_from = mon_dpi;
5200 else dpi_to = mon_dpi;
5202 if (dpi_from == dpi_to) return;
5203 cs->x = muldiv( cs->x, dpi_to, dpi_from );
5204 cs->y = muldiv( cs->y, dpi_to, dpi_from );
5205 cs->cx = muldiv( cs->cx, dpi_to, dpi_from );
5206 cs->cy = muldiv( cs->cy, dpi_to, dpi_from );
5209 /***********************************************************************
5210 * NtUserCreateWindowEx (win32u.@)
5212 HWND WINAPI NtUserCreateWindowEx( DWORD ex_style, UNICODE_STRING *class_name,
5213 UNICODE_STRING *version, UNICODE_STRING *window_name,
5214 DWORD style, INT x, INT y, INT cx, INT cy,
5215 HWND parent, HMENU menu, HINSTANCE instance, void *params,
5216 DWORD flags, HINSTANCE client_instance, DWORD unk, BOOL ansi )
5218 UINT win_dpi, thread_dpi = get_thread_dpi(), context;
5219 struct window_surface *surface;
5220 CBT_CREATEWNDW cbtc;
5221 HWND hwnd, owner = 0;
5222 CREATESTRUCTW cs;
5223 INT sw = SW_SHOW;
5224 RECT rect, visible_rect, surface_rect;
5225 WND *win;
5227 static const WCHAR messageW[] = {'M','e','s','s','a','g','e'};
5229 cs.lpCreateParams = params;
5230 cs.hInstance = client_instance ? client_instance : instance;
5231 cs.hMenu = menu;
5232 cs.hwndParent = parent;
5233 cs.style = style;
5234 cs.dwExStyle = ex_style;
5235 cs.lpszName = window_name ? window_name->Buffer : NULL;
5236 cs.lpszClass = class_name ? class_name->Buffer : NULL;
5237 cs.x = x;
5238 cs.y = y;
5239 cs.cx = cx;
5240 cs.cy = cy;
5242 /* Find the parent window */
5243 if (parent == HWND_MESSAGE)
5245 cs.hwndParent = parent = get_hwnd_message_parent();
5247 else if (parent)
5249 if ((cs.style & (WS_CHILD|WS_POPUP)) != WS_CHILD)
5251 owner = parent;
5252 parent = get_desktop_window();
5254 else
5256 DWORD parent_style = get_window_long( parent, GWL_EXSTYLE );
5257 if ((parent_style & WS_EX_LAYOUTRTL) && !(parent_style & WS_EX_NOINHERITLAYOUT))
5258 cs.dwExStyle |= WS_EX_LAYOUTRTL;
5261 else
5263 if ((cs.style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
5265 WARN( "No parent for child window\n" );
5266 RtlSetLastWin32Error( ERROR_TLW_WITH_WSCHILD );
5267 return 0; /* WS_CHILD needs a parent, but WS_POPUP doesn't */
5270 /* are we creating the desktop or HWND_MESSAGE parent itself? */
5271 if (class_name->Buffer != (LPCWSTR)DESKTOP_CLASS_ATOM &&
5272 (class_name->Length != sizeof(messageW) ||
5273 wcsnicmp( class_name->Buffer, messageW, ARRAYSIZE(messageW) )))
5275 if (get_process_layout() & LAYOUT_RTL) cs.dwExStyle |= WS_EX_LAYOUTRTL;
5276 parent = get_desktop_window();
5280 fix_cs_coordinates( &cs, &sw );
5281 cs.dwExStyle = fix_exstyle( cs.style, cs.dwExStyle );
5283 /* Create the window structure */
5285 style = cs.style & ~WS_VISIBLE;
5286 ex_style = cs.dwExStyle & ~WS_EX_LAYERED;
5287 if (!(win = create_window_handle( parent, owner, class_name, instance, ansi, style, ex_style )))
5288 return 0;
5289 hwnd = win->obj.handle;
5291 /* Fill the window structure */
5293 win->tid = GetCurrentThreadId();
5294 win->hInstance = cs.hInstance;
5295 win->text = NULL;
5296 win->dwStyle = style;
5297 win->dwExStyle = ex_style;
5298 win->wIDmenu = 0;
5299 win->helpContext = 0;
5300 win->pScroll = NULL;
5301 win->userdata = 0;
5302 win->hIcon = 0;
5303 win->hIconSmall = 0;
5304 win->hIconSmall2 = 0;
5305 win->hSysMenu = 0;
5307 win->min_pos.x = win->min_pos.y = -1;
5308 win->max_pos.x = win->max_pos.y = -1;
5309 SetRect( &win->normal_rect, cs.x, cs.y, cs.x + cs.cx, cs.y + cs.cy );
5311 if (win->dwStyle & WS_SYSMENU) NtUserSetSystemMenu( hwnd, 0 );
5313 win->imc = get_default_input_context();
5315 /* call the WH_CBT hook */
5317 release_win_ptr( win );
5318 cbtc.hwndInsertAfter = HWND_TOP;
5319 cbtc.lpcs = &cs;
5320 if (call_hooks( WH_CBT, HCBT_CREATEWND, (WPARAM)hwnd, (LPARAM)&cbtc, sizeof(cbtc) ))
5322 free_window_handle( hwnd );
5323 return 0;
5325 if (!(win = get_win_ptr( hwnd ))) return 0;
5328 * Correct the window styles.
5330 * It affects only the style loaded into the WND structure.
5333 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
5335 win->dwStyle |= WS_CLIPSIBLINGS;
5336 if (!(win->dwStyle & WS_POPUP)) win->dwStyle |= WS_CAPTION;
5339 win->dwExStyle = cs.dwExStyle;
5340 /* WS_EX_WINDOWEDGE depends on some other styles */
5341 if ((win->dwStyle & (WS_DLGFRAME | WS_THICKFRAME)) &&
5342 !(win->dwStyle & (WS_CHILD | WS_POPUP)))
5343 win->dwExStyle |= WS_EX_WINDOWEDGE;
5345 if (!(win->dwStyle & (WS_CHILD | WS_POPUP))) win->flags |= WIN_NEED_SIZE;
5347 SERVER_START_REQ( set_window_info )
5349 req->handle = wine_server_user_handle( hwnd );
5350 req->flags = SET_WIN_STYLE | SET_WIN_EXSTYLE | SET_WIN_INSTANCE | SET_WIN_UNICODE;
5351 req->style = win->dwStyle;
5352 req->ex_style = win->dwExStyle;
5353 req->instance = wine_server_client_ptr( win->hInstance );
5354 req->is_unicode = (win->flags & WIN_ISUNICODE) != 0;
5355 req->extra_offset = -1;
5356 wine_server_call( req );
5358 SERVER_END_REQ;
5360 /* Set the window menu */
5362 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
5364 if (cs.hMenu && !set_window_menu( hwnd, cs.hMenu ))
5366 release_win_ptr( win );
5367 free_window_handle( hwnd );
5368 return 0;
5371 else NtUserSetWindowLongPtr( hwnd, GWLP_ID, (ULONG_PTR)cs.hMenu, FALSE );
5373 win_dpi = NTUSER_DPI_CONTEXT_GET_DPI( win->dpi_context );
5374 release_win_ptr( win );
5376 if (parent) map_dpi_create_struct( &cs, thread_dpi, win_dpi );
5378 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
5380 /* send the WM_GETMINMAXINFO message and fix the size if needed */
5382 cx = cs.cx;
5383 cy = cs.cy;
5384 if ((cs.style & WS_THICKFRAME) || !(cs.style & (WS_POPUP | WS_CHILD)))
5386 MINMAXINFO info = get_min_max_info( hwnd );
5387 cx = max( min( cx, info.ptMaxTrackSize.x ), info.ptMinTrackSize.x );
5388 cy = max( min( cy, info.ptMaxTrackSize.y ), info.ptMinTrackSize.y );
5391 if (cx < 0) cx = 0;
5392 if (cy < 0) cy = 0;
5393 SetRect( &rect, cs.x, cs.y, cs.x + cx, cs.y + cy );
5394 /* check for wraparound */
5395 if (cs.x > 0x7fffffff - cx) rect.right = 0x7fffffff;
5396 if (cs.y > 0x7fffffff - cy) rect.bottom = 0x7fffffff;
5398 surface = create_window_surface( hwnd, SWP_NOZORDER | SWP_NOACTIVATE, FALSE, &rect, &rect, &visible_rect, &surface_rect );
5399 if (!apply_window_pos( hwnd, 0, SWP_NOZORDER | SWP_NOACTIVATE, surface, &rect, &rect, &visible_rect, NULL ))
5401 if (surface) window_surface_release( surface );
5402 goto failed;
5404 if (surface) window_surface_release( surface );
5406 /* send WM_NCCREATE */
5408 TRACE( "hwnd %p cs %d,%d %dx%d %s\n", hwnd, cs.x, cs.y, cs.cx, cs.cy, wine_dbgstr_rect(&rect) );
5409 if (!send_message_timeout( hwnd, WM_NCCREATE, 0, (LPARAM)&cs, SMTO_NORMAL, 0, ansi ))
5411 WARN( "%p: aborted by WM_NCCREATE\n", hwnd );
5412 goto failed;
5415 /* create default IME window */
5417 if (!is_desktop_window( hwnd ) && parent != get_hwnd_message_parent() &&
5418 register_imm_window( hwnd ))
5420 TRACE( "register IME window for %p\n", hwnd );
5421 win_set_flags( hwnd, WIN_HAS_IME_WIN, 0 );
5424 /* send WM_NCCALCSIZE */
5426 if (get_window_rects( hwnd, COORDS_PARENT, &rect, NULL, win_dpi ))
5428 /* yes, even if the CBT hook was called with HWND_TOP */
5429 HWND insert_after = (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) ? HWND_BOTTOM : HWND_TOP;
5430 RECT client_rect = rect;
5432 /* the rectangle is in screen coords for WM_NCCALCSIZE when wparam is FALSE */
5433 map_window_points( parent, 0, (POINT *)&client_rect, 2, win_dpi );
5434 send_message( hwnd, WM_NCCALCSIZE, FALSE, (LPARAM)&client_rect );
5435 map_window_points( 0, parent, (POINT *)&client_rect, 2, win_dpi );
5437 surface = create_window_surface( hwnd, SWP_NOACTIVATE, FALSE, &rect, &client_rect, &visible_rect, &surface_rect );
5438 apply_window_pos( hwnd, insert_after, SWP_NOACTIVATE, surface, &rect, &client_rect, &visible_rect, NULL );
5439 if (surface) window_surface_release( surface );
5441 else goto failed;
5443 /* send WM_CREATE */
5444 if (send_message_timeout( hwnd, WM_CREATE, 0, (LPARAM)&cs, SMTO_NORMAL, 0, ansi ) == -1)
5445 goto failed;
5447 /* call the driver */
5449 if (!user_driver->pCreateWindow( hwnd )) goto failed;
5451 NtUserNotifyWinEvent( EVENT_OBJECT_CREATE, hwnd, OBJID_WINDOW, 0 );
5453 /* send the size messages */
5455 if (!(win_get_flags( hwnd ) & WIN_NEED_SIZE))
5457 get_window_rects( hwnd, COORDS_PARENT, NULL, &rect, win_dpi );
5458 send_message( hwnd, WM_SIZE, SIZE_RESTORED,
5459 MAKELONG(rect.right-rect.left, rect.bottom-rect.top));
5460 send_message( hwnd, WM_MOVE, 0, MAKELONG( rect.left, rect.top ) );
5463 /* Show the window, maximizing or minimizing if needed */
5465 style = set_window_style( hwnd, 0, WS_MAXIMIZE | WS_MINIMIZE );
5466 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
5468 RECT new_pos;
5469 UINT sw_flags = (style & WS_MINIMIZE) ? SW_MINIMIZE : SW_MAXIMIZE;
5471 sw_flags = window_min_maximize( hwnd, sw_flags, &new_pos );
5472 sw_flags |= SWP_FRAMECHANGED; /* Frame always gets changed */
5473 if (!(style & WS_VISIBLE) || (style & WS_CHILD) || get_active_window())
5474 sw_flags |= SWP_NOACTIVATE;
5475 NtUserSetWindowPos( hwnd, 0, new_pos.left, new_pos.top, new_pos.right - new_pos.left,
5476 new_pos.bottom - new_pos.top, sw_flags );
5479 /* Notify the parent window only */
5481 send_parent_notify( hwnd, WM_CREATE );
5482 if (!is_window( hwnd ))
5484 set_thread_dpi_awareness_context( context );
5485 return 0;
5488 if (parent == get_desktop_window())
5489 NtUserPostMessage( parent, WM_PARENTNOTIFY, WM_CREATE, (LPARAM)hwnd );
5491 if (cs.style & WS_VISIBLE)
5493 if (cs.style & WS_MAXIMIZE)
5494 sw = SW_SHOW;
5495 else if (cs.style & WS_MINIMIZE)
5496 sw = SW_SHOWMINIMIZED;
5498 NtUserShowWindow( hwnd, sw );
5499 if (cs.dwExStyle & WS_EX_MDICHILD)
5501 send_message( cs.hwndParent, WM_MDIREFRESHMENU, 0, 0 );
5502 /* ShowWindow won't activate child windows */
5503 NtUserSetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE );
5507 /* Call WH_SHELL hook */
5509 if (!(get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) && !get_window_relative( hwnd, GW_OWNER ))
5510 call_hooks( WH_SHELL, HSHELL_WINDOWCREATED, (WPARAM)hwnd, 0, 0 );
5512 TRACE( "created window %p\n", hwnd );
5513 set_thread_dpi_awareness_context( context );
5514 return hwnd;
5516 failed:
5517 destroy_window( hwnd );
5518 set_thread_dpi_awareness_context( context );
5519 return 0;
5522 static void *get_dialog_info( HWND hwnd )
5524 WND *win;
5525 void *ret;
5527 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP)
5529 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
5530 return NULL;
5533 ret = win->dlgInfo;
5534 release_win_ptr( win );
5535 return ret;
5538 static BOOL set_dialog_info( HWND hwnd, void *info )
5540 WND *win;
5542 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
5543 win->dlgInfo = info;
5544 release_win_ptr( win );
5545 return TRUE;
5548 /*****************************************************************************
5549 * NtUserCallHwnd (win32u.@)
5551 ULONG_PTR WINAPI NtUserCallHwnd( HWND hwnd, DWORD code )
5553 switch (code)
5555 case NtUserCallHwnd_ActivateOtherWindow:
5556 activate_other_window( hwnd );
5557 return 0;
5559 case NtUserCallHwnd_ArrangeIconicWindows:
5560 return arrange_iconic_windows( hwnd );
5562 case NtUserCallHwnd_DrawMenuBar:
5563 return draw_menu_bar( hwnd );
5565 case NtUserCallHwnd_GetDefaultImeWindow:
5566 return HandleToUlong( get_default_ime_window( hwnd ));
5568 case NtUserCallHwnd_GetDpiForWindow:
5569 return get_dpi_for_window( hwnd );
5571 case NtUserCallHwnd_GetParent:
5572 return HandleToUlong( get_parent( hwnd ));
5574 case NtUserCallHwnd_GetDialogInfo:
5575 return (ULONG_PTR)get_dialog_info( hwnd );
5577 case NtUserCallHwnd_GetMDIClientInfo:
5578 if (!(win_get_flags( hwnd ) & WIN_ISMDICLIENT)) return 0;
5579 return get_window_long_ptr( hwnd, sizeof(void *), FALSE );
5581 case NtUserCallHwnd_GetWindowContextHelpId:
5582 return get_window_context_help_id( hwnd );
5584 case NtUserCallHwnd_GetWindowDpiAwarenessContext:
5585 return get_window_dpi_awareness_context( hwnd );
5587 case NtUserCallHwnd_GetWindowInputContext:
5588 return HandleToUlong( get_window_input_context( hwnd ));
5590 case NtUserCallHwnd_GetWindowSysSubMenu:
5591 return HandleToUlong( get_window_sys_sub_menu( hwnd ));
5593 case NtUserCallHwnd_GetWindowTextLength:
5594 return get_server_window_text( hwnd, NULL, 0 );
5596 case NtUserCallHwnd_IsWindow:
5597 return is_window( hwnd );
5599 case NtUserCallHwnd_IsWindowEnabled:
5600 return is_window_enabled( hwnd );
5602 case NtUserCallHwnd_IsWindowUnicode:
5603 return is_window_unicode( hwnd );
5605 case NtUserCallHwnd_IsWindowVisible:
5606 return is_window_visible( hwnd );
5608 case NtUserCallHwnd_SetForegroundWindow:
5609 return set_foreground_window( hwnd, FALSE );
5611 case NtUserCallHwnd_SetProgmanWindow:
5612 return HandleToUlong( set_progman_window( hwnd ));
5614 case NtUserCallHwnd_SetTaskmanWindow:
5615 return HandleToUlong( set_taskman_window( hwnd ));
5617 /* temporary exports */
5618 case NtUserGetFullWindowHandle:
5619 return HandleToUlong( get_full_window_handle( hwnd ));
5621 case NtUserIsCurrentProcessWindow:
5622 return HandleToUlong( is_current_process_window( hwnd ));
5624 case NtUserIsCurrentThreadWindow:
5625 return HandleToUlong( is_current_thread_window( hwnd ));
5627 default:
5628 FIXME( "invalid code %u\n", (int)code );
5629 return 0;
5633 /*****************************************************************************
5634 * NtUserCallHwndParam (win32u.@)
5636 ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code )
5638 switch (code)
5640 case NtUserCallHwndParam_ClientToScreen:
5641 return client_to_screen( hwnd, (POINT *)param );
5643 case NtUserCallHwndParam_EnableWindow:
5644 return enable_window( hwnd, param );
5646 case NtUserCallHwndParam_GetChildRect:
5647 return get_window_rects( hwnd, COORDS_PARENT, (RECT *)param, NULL, get_thread_dpi() );
5649 case NtUserCallHwndParam_GetClassLongA:
5650 return get_class_long( hwnd, param, TRUE );
5652 case NtUserCallHwndParam_GetClassLongW:
5653 return get_class_long( hwnd, param, FALSE );
5655 case NtUserCallHwndParam_GetClassLongPtrA:
5656 return get_class_long_ptr( hwnd, param, TRUE );
5658 case NtUserCallHwndParam_GetClassLongPtrW:
5659 return get_class_long_ptr( hwnd, param, FALSE );
5661 case NtUserCallHwndParam_GetClassWord:
5662 return get_class_word( hwnd, param );
5664 case NtUserCallHwndParam_GetScrollInfo:
5666 struct get_scroll_info_params *params = (void *)param;
5667 return get_scroll_info( hwnd, params->bar, params->info );
5670 case NtUserCallHwndParam_GetWindowInfo:
5671 return get_window_info( hwnd, (WINDOWINFO *)param );
5673 case NtUserCallHwndParam_GetWindowLongA:
5674 return get_window_long_size( hwnd, param, sizeof(LONG), TRUE );
5676 case NtUserCallHwndParam_GetWindowLongW:
5677 return get_window_long( hwnd, param );
5679 case NtUserCallHwndParam_GetWindowLongPtrA:
5680 return get_window_long_ptr( hwnd, param, TRUE );
5682 case NtUserCallHwndParam_GetWindowLongPtrW:
5683 return get_window_long_ptr( hwnd, param, FALSE );
5685 case NtUserCallHwndParam_GetWindowRects:
5687 struct get_window_rects_params *params = (void *)param;
5688 return params->client ? get_client_rect( hwnd, params->rect, params->dpi )
5689 : get_window_rect( hwnd, params->rect, params->dpi );
5692 case NtUserCallHwndParam_GetWindowRelative:
5693 return HandleToUlong( get_window_relative( hwnd, param ));
5695 case NtUserCallHwndParam_GetWindowThread:
5696 return get_window_thread( hwnd, (DWORD *)param );
5698 case NtUserCallHwndParam_GetWindowWord:
5699 return get_window_word( hwnd, param );
5701 case NtUserCallHwndParam_IsChild:
5702 return is_child( hwnd, UlongToHandle(param) );
5704 case NtUserCallHwndParam_KillSystemTimer:
5705 return kill_system_timer( hwnd, param );
5707 case NtUserCallHwndParam_MapWindowPoints:
5709 struct map_window_points_params *params = (void *)param;
5710 return map_window_points( hwnd, params->hwnd_to, params->points, params->count, params->dpi );
5713 case NtUserCallHwndParam_MirrorRgn:
5714 return mirror_window_region( hwnd, UlongToHandle(param) );
5716 case NtUserCallHwndParam_MonitorFromWindow:
5717 return HandleToUlong( monitor_from_window( hwnd, param, get_thread_dpi() ));
5719 case NtUserCallHwndParam_ScreenToClient:
5720 return screen_to_client( hwnd, (POINT *)param );
5722 case NtUserCallHwndParam_SetDialogInfo:
5723 return set_dialog_info( hwnd, (void *)param );
5725 case NtUserCallHwndParam_SetMDIClientInfo:
5726 NtUserSetWindowLongPtr( hwnd, sizeof(void *), param, FALSE );
5727 return win_set_flags( hwnd, WIN_ISMDICLIENT, 0 );
5729 case NtUserCallHwndParam_SetWindowContextHelpId:
5730 return set_window_context_help_id( hwnd, param );
5732 case NtUserCallHwndParam_ShowOwnedPopups:
5733 return show_owned_popups( hwnd, param );
5735 case NtUserCallHwndParam_SendHardwareInput:
5737 struct send_hardware_input_params *params = (void *)param;
5738 return send_hardware_message( hwnd, params->flags, params->input, params->lparam );
5741 /* temporary exports */
5742 case NtUserSetWindowStyle:
5744 STYLESTRUCT *style = (void *)param;
5745 return set_window_style( hwnd, style->styleNew, style->styleOld );
5748 default:
5749 FIXME( "invalid code %u\n", (int)code );
5750 return 0;
5754 /*******************************************************************
5755 * NtUserDragDetect (win32u.@)
5757 BOOL WINAPI NtUserDragDetect( HWND hwnd, int x, int y )
5759 WORD width, height;
5760 RECT rect;
5761 MSG msg;
5763 TRACE( "%p (%d,%d)\n", hwnd, x, y );
5765 if (!(NtUserGetKeyState( VK_LBUTTON ) & 0x8000)) return FALSE;
5767 width = get_system_metrics( SM_CXDRAG );
5768 height = get_system_metrics( SM_CYDRAG );
5769 SetRect( &rect, x - width, y - height, x + width, y + height );
5771 NtUserSetCapture( hwnd );
5773 for (;;)
5775 while (NtUserPeekMessage( &msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE ))
5777 if (msg.message == WM_LBUTTONUP)
5779 release_capture();
5780 return FALSE;
5782 if (msg.message == WM_MOUSEMOVE)
5784 POINT tmp;
5785 tmp.x = (short)LOWORD( msg.lParam );
5786 tmp.y = (short)HIWORD( msg.lParam );
5787 if (!PtInRect( &rect, tmp ))
5789 release_capture();
5790 return TRUE;
5794 NtUserMsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, 0 );
5796 return FALSE;
5799 /*******************************************************************
5800 * NtUserDragObject (win32u.@)
5802 DWORD WINAPI NtUserDragObject( HWND parent, HWND hwnd, UINT fmt, ULONG_PTR data, HCURSOR cursor )
5804 FIXME( "%p, %p, %u, %#lx, %p stub!\n", parent, hwnd, fmt, data, cursor );
5806 return 0;
5810 HWND get_shell_window(void)
5812 HWND hwnd = 0;
5814 SERVER_START_REQ(set_global_windows)
5816 req->flags = 0;
5817 if (!wine_server_call_err(req))
5818 hwnd = wine_server_ptr_handle( reply->old_shell_window );
5820 SERVER_END_REQ;
5822 return hwnd;
5825 /***********************************************************************
5826 * NtUserSetShellWindowEx (win32u.@)
5828 BOOL WINAPI NtUserSetShellWindowEx( HWND shell, HWND list_view )
5830 BOOL ret;
5832 /* shell = Progman[Program Manager]
5833 * |-> SHELLDLL_DefView
5834 * list_view = | |-> SysListView32
5835 * | | |-> tooltips_class32
5836 * | |
5837 * | |-> SysHeader32
5839 * |-> ProxyTarget
5842 if (get_shell_window())
5843 return FALSE;
5845 if (get_window_long( shell, GWL_EXSTYLE ) & WS_EX_TOPMOST)
5846 return FALSE;
5848 if (list_view != shell && (get_window_long( list_view, GWL_EXSTYLE ) & WS_EX_TOPMOST))
5849 return FALSE;
5851 if (list_view && list_view != shell)
5852 NtUserSetWindowPos( list_view, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
5854 NtUserSetWindowPos( shell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
5856 SERVER_START_REQ(set_global_windows)
5858 req->flags = SET_GLOBAL_SHELL_WINDOWS;
5859 req->shell_window = wine_server_user_handle( shell );
5860 req->shell_listview = wine_server_user_handle( list_view );
5861 ret = !wine_server_call_err(req);
5863 SERVER_END_REQ;
5864 return ret;
5867 HWND get_progman_window(void)
5869 HWND ret = 0;
5871 SERVER_START_REQ(set_global_windows)
5873 req->flags = 0;
5874 if (!wine_server_call_err(req))
5875 ret = wine_server_ptr_handle( reply->old_progman_window );
5877 SERVER_END_REQ;
5878 return ret;
5881 HWND set_progman_window( HWND hwnd )
5883 SERVER_START_REQ(set_global_windows)
5885 req->flags = SET_GLOBAL_PROGMAN_WINDOW;
5886 req->progman_window = wine_server_user_handle( hwnd );
5887 if (wine_server_call_err( req )) hwnd = 0;
5889 SERVER_END_REQ;
5890 return hwnd;
5893 HWND get_taskman_window(void)
5895 HWND ret = 0;
5897 SERVER_START_REQ(set_global_windows)
5899 req->flags = 0;
5900 if (!wine_server_call_err(req))
5901 ret = wine_server_ptr_handle( reply->old_taskman_window );
5903 SERVER_END_REQ;
5904 return ret;
5907 HWND set_taskman_window( HWND hwnd )
5909 /* hwnd = MSTaskSwWClass
5910 * |-> SysTabControl32
5912 SERVER_START_REQ(set_global_windows)
5914 req->flags = SET_GLOBAL_TASKMAN_WINDOW;
5915 req->taskman_window = wine_server_user_handle( hwnd );
5916 if (wine_server_call_err( req )) hwnd = 0;
5918 SERVER_END_REQ;
5919 return hwnd;