d3dx9: Unify calling parse_mesh helper functions.
[wine.git] / dlls / win32u / window.c
blob69dd8caba5d226ecc447c7c0bc8a9c044c4a6af0
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 DPI_AWARENESS_CONTEXT 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 = reply->dpi;
454 win->dpi_awareness = reply->awareness;
458 SERVER_END_REQ;
459 release_win_ptr( win );
460 if (!ret) return 0;
462 get_window_rects( hwnd, COORDS_SCREEN, &new_screen_rect, NULL, 0 );
463 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
465 user_driver->pSetParent( full_handle, parent, old_parent );
467 winpos.hwnd = hwnd;
468 winpos.hwndInsertAfter = HWND_TOP;
469 winpos.x = window_rect.left;
470 winpos.y = window_rect.top;
471 winpos.cx = 0;
472 winpos.cy = 0;
473 winpos.flags = SWP_NOSIZE;
475 set_window_pos( &winpos, new_screen_rect.left - old_screen_rect.left,
476 new_screen_rect.top - old_screen_rect.top );
478 if (was_visible) NtUserShowWindow( hwnd, SW_SHOW );
480 SetThreadDpiAwarenessContext( context );
481 return old_parent;
484 /* see GetWindow */
485 HWND get_window_relative( HWND hwnd, UINT rel )
487 HWND retval = 0;
489 if (rel == GW_OWNER) /* this one may be available locally */
491 WND *win = get_win_ptr( hwnd );
492 if (!win)
494 RtlSetLastWin32Error( ERROR_INVALID_HANDLE );
495 return 0;
497 if (win == WND_DESKTOP) return 0;
498 if (win != WND_OTHER_PROCESS)
500 retval = win->owner;
501 release_win_ptr( win );
502 return retval;
504 /* else fall through to server call */
507 SERVER_START_REQ( get_window_tree )
509 req->handle = wine_server_user_handle( hwnd );
510 if (!wine_server_call_err( req ))
512 switch(rel)
514 case GW_HWNDFIRST:
515 retval = wine_server_ptr_handle( reply->first_sibling );
516 break;
517 case GW_HWNDLAST:
518 retval = wine_server_ptr_handle( reply->last_sibling );
519 break;
520 case GW_HWNDNEXT:
521 retval = wine_server_ptr_handle( reply->next_sibling );
522 break;
523 case GW_HWNDPREV:
524 retval = wine_server_ptr_handle( reply->prev_sibling );
525 break;
526 case GW_OWNER:
527 retval = wine_server_ptr_handle( reply->owner );
528 break;
529 case GW_CHILD:
530 retval = wine_server_ptr_handle( reply->first_child );
531 break;
535 SERVER_END_REQ;
536 return retval;
539 /*******************************************************************
540 * list_window_parents
542 * Build an array of all parents of a given window, starting with
543 * the immediate parent. The array must be freed with free().
545 static HWND *list_window_parents( HWND hwnd )
547 WND *win;
548 HWND current, *list;
549 int i, pos = 0, size = 16, count;
551 if (!(list = malloc( size * sizeof(HWND) ))) return NULL;
553 current = hwnd;
554 for (;;)
556 if (!(win = get_win_ptr( current ))) goto empty;
557 if (win == WND_OTHER_PROCESS) break; /* need to do it the hard way */
558 if (win == WND_DESKTOP)
560 if (!pos) goto empty;
561 list[pos] = 0;
562 return list;
564 list[pos] = current = win->parent;
565 release_win_ptr( win );
566 if (!current) return list;
567 if (++pos == size - 1)
569 /* need to grow the list */
570 HWND *new_list = realloc( list, (size + 16) * sizeof(HWND) );
571 if (!new_list) goto empty;
572 list = new_list;
573 size += 16;
577 /* at least one parent belongs to another process, have to query the server */
579 for (;;)
581 count = 0;
582 SERVER_START_REQ( get_window_parents )
584 req->handle = wine_server_user_handle( hwnd );
585 wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
586 if (!wine_server_call( req )) count = reply->count;
588 SERVER_END_REQ;
589 if (!count) goto empty;
590 if (size > count)
592 /* start from the end since HWND is potentially larger than user_handle_t */
593 for (i = count - 1; i >= 0; i--)
594 list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
595 list[count] = 0;
596 return list;
598 free( list );
599 size = count + 1;
600 if (!(list = malloc( size * sizeof(HWND) ))) return NULL;
603 empty:
604 free( list );
605 return NULL;
608 /*******************************************************************
609 * list_window_children
611 * Build an array of the children of a given window. The array must be
612 * freed with HeapFree. Returns NULL when no windows are found.
614 HWND *list_window_children( HDESK desktop, HWND hwnd, UNICODE_STRING *class, DWORD tid )
616 HWND *list;
617 int i, size = 128;
618 ATOM atom = class ? get_int_atom_value( class ) : 0;
620 /* empty class is not the same as NULL class */
621 if (!atom && class && !class->Length) return NULL;
623 for (;;)
625 int count = 0;
627 if (!(list = malloc( size * sizeof(HWND) ))) break;
629 SERVER_START_REQ( get_window_children )
631 req->desktop = wine_server_obj_handle( desktop );
632 req->parent = wine_server_user_handle( hwnd );
633 req->tid = tid;
634 req->atom = atom;
635 if (!atom && class) wine_server_add_data( req, class->Buffer, class->Length );
636 wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
637 if (!wine_server_call( req )) count = reply->count;
639 SERVER_END_REQ;
640 if (count && count < size)
642 /* start from the end since HWND is potentially larger than user_handle_t */
643 for (i = count - 1; i >= 0; i--)
644 list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
645 list[count] = 0;
646 return list;
648 free( list );
649 if (!count) break;
650 size = count + 1; /* restart with a large enough buffer */
652 return NULL;
655 /*****************************************************************
656 * NtUserGetAncestor (win32u.@)
658 HWND WINAPI NtUserGetAncestor( HWND hwnd, UINT type )
660 HWND *list, ret = 0;
661 WND *win;
663 switch(type)
665 case GA_PARENT:
666 if (!(win = get_win_ptr( hwnd )))
668 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
669 return 0;
671 if (win == WND_DESKTOP) return 0;
672 if (win != WND_OTHER_PROCESS)
674 ret = win->parent;
675 release_win_ptr( win );
677 else /* need to query the server */
679 SERVER_START_REQ( get_window_tree )
681 req->handle = wine_server_user_handle( hwnd );
682 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->parent );
684 SERVER_END_REQ;
686 break;
688 case GA_ROOT:
689 if (!(list = list_window_parents( hwnd ))) return 0;
691 if (!list[0] || !list[1]) ret = get_full_window_handle( hwnd ); /* top-level window */
692 else
694 int count = 2;
695 while (list[count]) count++;
696 ret = list[count - 2]; /* get the one before the desktop */
698 free( list );
699 break;
701 case GA_ROOTOWNER:
702 if (is_desktop_window( hwnd )) return 0;
703 ret = get_full_window_handle( hwnd );
704 for (;;)
706 HWND parent = get_parent( ret );
707 if (!parent) break;
708 ret = parent;
710 break;
712 return ret;
715 /* see IsChild */
716 BOOL is_child( HWND parent, HWND child )
718 HWND *list;
719 int i;
720 BOOL ret = FALSE;
722 if (!(get_window_long( child, GWL_STYLE ) & WS_CHILD)) return FALSE;
723 if (!(list = list_window_parents( child ))) return FALSE;
724 parent = get_full_window_handle( parent );
725 for (i = 0; list[i]; i++)
727 if (list[i] == parent)
729 ret = list[i] && list[i+1];
730 break;
732 if (!(get_window_long( list[i], GWL_STYLE ) & WS_CHILD)) break;
734 free( list );
735 return ret;
738 /* see IsWindowVisible */
739 BOOL is_window_visible( HWND hwnd )
741 HWND *list;
742 BOOL retval = TRUE;
743 int i;
745 if (!(get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE)) return FALSE;
746 if (!(list = list_window_parents( hwnd ))) return TRUE;
747 if (list[0])
749 for (i = 0; list[i+1]; i++)
750 if (!(get_window_long( list[i], GWL_STYLE ) & WS_VISIBLE)) break;
751 retval = !list[i+1] && (list[i] == get_desktop_window()); /* top message window isn't visible */
753 free( list );
754 return retval;
757 /***********************************************************************
758 * is_window_drawable
760 * hwnd is drawable when it is visible, all parents are not
761 * minimized, and it is itself not minimized unless we are
762 * trying to draw its default class icon.
764 BOOL is_window_drawable( HWND hwnd, BOOL icon )
766 HWND *list;
767 BOOL retval = TRUE;
768 int i;
769 LONG style = get_window_long( hwnd, GWL_STYLE );
771 if (!(style & WS_VISIBLE)) return FALSE;
772 if ((style & WS_MINIMIZE) && icon && get_class_long_ptr( hwnd, GCLP_HICON, FALSE )) return FALSE;
774 if (!(list = list_window_parents( hwnd ))) return TRUE;
775 if (list[0])
777 for (i = 0; list[i+1]; i++)
778 if ((get_window_long( list[i], GWL_STYLE ) & (WS_VISIBLE|WS_MINIMIZE)) != WS_VISIBLE)
779 break;
780 retval = !list[i+1] && (list[i] == get_desktop_window()); /* top message window isn't visible */
782 free( list );
783 return retval;
786 /* see IsWindowUnicode */
787 BOOL is_window_unicode( HWND hwnd )
789 WND *win;
790 BOOL ret = FALSE;
792 if (!(win = get_win_ptr(hwnd))) return FALSE;
794 if (win == WND_DESKTOP) return TRUE;
796 if (win != WND_OTHER_PROCESS)
798 ret = (win->flags & WIN_ISUNICODE) != 0;
799 release_win_ptr( win );
801 else
803 SERVER_START_REQ( get_window_info )
805 req->handle = wine_server_user_handle( hwnd );
806 if (!wine_server_call_err( req )) ret = reply->is_unicode;
808 SERVER_END_REQ;
810 return ret;
813 /* see EnableWindow */
814 BOOL enable_window( HWND hwnd, BOOL enable )
816 BOOL ret;
818 if (is_broadcast(hwnd))
820 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
821 return FALSE;
824 TRACE( "( %p, %d )\n", hwnd, enable );
826 if (enable)
828 ret = (set_window_style( hwnd, 0, WS_DISABLED ) & WS_DISABLED) != 0;
829 if (ret) send_message( hwnd, WM_ENABLE, TRUE, 0 );
831 else
833 send_message( hwnd, WM_CANCELMODE, 0, 0 );
835 ret = (set_window_style( hwnd, WS_DISABLED, 0 ) & WS_DISABLED) != 0;
836 if (!ret)
838 if (hwnd == get_focus())
839 NtUserSetFocus( 0 ); /* A disabled window can't have the focus */
841 send_message( hwnd, WM_ENABLE, FALSE, 0 );
844 return ret;
847 /* see IsWindowEnabled */
848 BOOL is_window_enabled( HWND hwnd )
850 LONG ret;
852 RtlSetLastWin32Error( NO_ERROR );
853 ret = get_window_long( hwnd, GWL_STYLE );
854 if (!ret && RtlGetLastWin32Error() != NO_ERROR) return FALSE;
855 return !(ret & WS_DISABLED);
858 /* see GetWindowDpiAwarenessContext */
859 DPI_AWARENESS_CONTEXT get_window_dpi_awareness_context( HWND hwnd )
861 DPI_AWARENESS_CONTEXT ret = 0;
862 WND *win;
864 if (!(win = get_win_ptr( hwnd )))
866 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
867 return 0;
869 if (win == WND_DESKTOP) return DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE;
870 if (win != WND_OTHER_PROCESS)
872 ret = ULongToHandle( win->dpi_awareness | 0x10 );
873 release_win_ptr( win );
875 else
877 SERVER_START_REQ( get_window_info )
879 req->handle = wine_server_user_handle( hwnd );
880 if (!wine_server_call_err( req )) ret = ULongToHandle( reply->awareness | 0x10 );
882 SERVER_END_REQ;
884 return ret;
887 /* see GetDpiForWindow */
888 UINT get_dpi_for_window( HWND hwnd )
890 WND *win;
891 UINT ret = 0;
893 if (!(win = get_win_ptr( hwnd )))
895 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
896 return 0;
898 if (win == WND_DESKTOP)
900 POINT pt = { 0, 0 };
901 return get_monitor_dpi( monitor_from_point( pt, MONITOR_DEFAULTTOPRIMARY, 0 ));
903 if (win != WND_OTHER_PROCESS)
905 ret = win->dpi;
906 if (!ret) ret = get_win_monitor_dpi( hwnd );
907 release_win_ptr( win );
909 else
911 SERVER_START_REQ( get_window_info )
913 req->handle = wine_server_user_handle( hwnd );
914 if (!wine_server_call_err( req )) ret = reply->dpi;
916 SERVER_END_REQ;
918 return ret;
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;
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_STYLE && ((style.styleOld ^ style.styleNew) & WS_VISIBLE)) ||
1393 (offset == GWL_EXSTYLE && ((style.styleOld ^ style.styleNew) & WS_EX_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) 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)
1672 window_rect->left = reply->window.left;
1673 window_rect->top = reply->window.top;
1674 window_rect->right = reply->window.right;
1675 window_rect->bottom = reply->window.bottom;
1677 if (client_rect)
1679 client_rect->left = reply->client.left;
1680 client_rect->top = reply->client.top;
1681 client_rect->right = reply->client.right;
1682 client_rect->bottom = reply->client.bottom;
1686 SERVER_END_REQ;
1687 return ret;
1690 /* see GetWindowRect */
1691 BOOL get_window_rect( HWND hwnd, RECT *rect, UINT dpi )
1693 return get_window_rects( hwnd, COORDS_SCREEN, rect, NULL, dpi );
1696 /* see GetClientRect */
1697 BOOL get_client_rect( HWND hwnd, RECT *rect )
1699 return get_window_rects( hwnd, COORDS_CLIENT, NULL, rect, get_thread_dpi() );
1702 /* see GetWindowInfo */
1703 static BOOL get_window_info( HWND hwnd, WINDOWINFO *info )
1706 if (!info || !get_window_rects( hwnd, COORDS_SCREEN, &info->rcWindow,
1707 &info->rcClient, get_thread_dpi() ))
1708 return FALSE;
1710 info->dwStyle = get_window_long( hwnd, GWL_STYLE );
1711 info->dwExStyle = get_window_long( hwnd, GWL_EXSTYLE );
1712 info->dwWindowStatus = get_active_window() == hwnd ? WS_ACTIVECAPTION : 0;
1713 info->cxWindowBorders = info->rcClient.left - info->rcWindow.left;
1714 info->cyWindowBorders = info->rcWindow.bottom - info->rcClient.bottom;
1715 info->atomWindowType = get_class_long( hwnd, GCW_ATOM, FALSE );
1716 info->wCreatorVersion = 0x0400;
1717 return TRUE;
1720 /***********************************************************************
1721 * update_surface_region
1723 static void update_surface_region( HWND hwnd )
1725 NTSTATUS status;
1726 HRGN region = 0;
1727 RGNDATA *data;
1728 size_t size = 256;
1729 WND *win = get_win_ptr( hwnd );
1731 if (!win || win == WND_DESKTOP || win == WND_OTHER_PROCESS) return;
1732 if (!win->surface) goto done;
1736 if (!(data = malloc( FIELD_OFFSET( RGNDATA, Buffer[size] )))) goto done;
1738 SERVER_START_REQ( get_surface_region )
1740 req->window = wine_server_user_handle( hwnd );
1741 wine_server_set_reply( req, data->Buffer, size );
1742 if (!(status = wine_server_call( req )))
1744 size_t reply_size = wine_server_reply_size( reply );
1745 if (reply_size)
1747 data->rdh.dwSize = sizeof(data->rdh);
1748 data->rdh.iType = RDH_RECTANGLES;
1749 data->rdh.nCount = reply_size / sizeof(RECT);
1750 data->rdh.nRgnSize = reply_size;
1751 region = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1752 NtGdiOffsetRgn( region, -reply->visible_rect.left, -reply->visible_rect.top );
1755 else size = reply->total_size;
1757 SERVER_END_REQ;
1758 free( data );
1759 } while (status == STATUS_BUFFER_OVERFLOW);
1761 if (status) goto done;
1763 win->surface->funcs->set_region( win->surface, region );
1764 if (region) NtGdiDeleteObjectApp( region );
1766 done:
1767 release_win_ptr( win );
1770 /***********************************************************************
1771 * apply_window_pos
1773 * Backend implementation of SetWindowPos.
1775 static BOOL apply_window_pos( HWND hwnd, HWND insert_after, UINT swp_flags,
1776 const RECT *window_rect, const RECT *client_rect, const RECT *valid_rects )
1778 WND *win;
1779 HWND surface_win = 0, parent = NtUserGetAncestor( hwnd, GA_PARENT );
1780 BOOL ret, needs_update = FALSE;
1781 RECT visible_rect, old_visible_rect, old_window_rect, old_client_rect, extra_rects[3];
1782 struct window_surface *old_surface, *new_surface = NULL;
1784 if (!parent || parent == get_desktop_window())
1786 new_surface = &dummy_surface; /* provide a default surface for top-level windows */
1787 window_surface_add_ref( new_surface );
1789 visible_rect = *window_rect;
1790 if (!(ret = user_driver->pWindowPosChanging( hwnd, insert_after, swp_flags,
1791 window_rect, client_rect, &visible_rect, &new_surface )))
1793 if (IsRectEmpty( window_rect )) visible_rect = *window_rect;
1794 else
1796 visible_rect = get_virtual_screen_rect( get_thread_dpi() );
1797 intersect_rect( &visible_rect, &visible_rect, window_rect );
1801 get_window_rects( hwnd, COORDS_SCREEN, &old_window_rect, NULL, get_thread_dpi() );
1802 if (IsRectEmpty( &valid_rects[0] )) valid_rects = NULL;
1804 if (!(win = get_win_ptr( hwnd )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS)
1806 if (new_surface) window_surface_release( new_surface );
1807 return FALSE;
1810 /* create or update window surface for top-level windows if the driver doesn't implement WindowPosChanging */
1811 if (!ret && new_surface && !IsRectEmpty( &visible_rect ) &&
1812 (!(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED) ||
1813 NtUserGetLayeredWindowAttributes( hwnd, NULL, NULL, NULL )))
1815 window_surface_release( new_surface );
1816 if ((new_surface = win->surface)) window_surface_add_ref( new_surface );
1817 create_offscreen_window_surface( &visible_rect, &new_surface );
1820 old_visible_rect = win->visible_rect;
1821 old_client_rect = win->client_rect;
1822 old_surface = win->surface;
1823 if (old_surface != new_surface) swp_flags |= SWP_FRAMECHANGED; /* force refreshing non-client area */
1824 if (new_surface == &dummy_surface) swp_flags |= SWP_NOREDRAW;
1825 else if (old_surface == &dummy_surface)
1827 swp_flags |= SWP_NOCOPYBITS;
1828 valid_rects = NULL;
1831 SERVER_START_REQ( set_window_pos )
1833 req->handle = wine_server_user_handle( hwnd );
1834 req->previous = wine_server_user_handle( insert_after );
1835 req->swp_flags = swp_flags;
1836 req->window.left = window_rect->left;
1837 req->window.top = window_rect->top;
1838 req->window.right = window_rect->right;
1839 req->window.bottom = window_rect->bottom;
1840 req->client.left = client_rect->left;
1841 req->client.top = client_rect->top;
1842 req->client.right = client_rect->right;
1843 req->client.bottom = client_rect->bottom;
1844 if (!EqualRect( window_rect, &visible_rect ) || new_surface || valid_rects)
1846 extra_rects[0] = extra_rects[1] = visible_rect;
1847 if (new_surface)
1849 extra_rects[1] = new_surface->rect;
1850 OffsetRect( &extra_rects[1], visible_rect.left, visible_rect.top );
1852 if (valid_rects) extra_rects[2] = valid_rects[0];
1853 else SetRectEmpty( &extra_rects[2] );
1854 wine_server_add_data( req, extra_rects, sizeof(extra_rects) );
1856 if (new_surface) req->paint_flags |= SET_WINPOS_PAINT_SURFACE;
1857 if (win->pixel_format || win->internal_pixel_format)
1858 req->paint_flags |= SET_WINPOS_PIXEL_FORMAT;
1860 if ((ret = !wine_server_call( req )))
1862 win->dwStyle = reply->new_style;
1863 win->dwExStyle = reply->new_ex_style;
1864 win->window_rect = *window_rect;
1865 win->client_rect = *client_rect;
1866 win->visible_rect = visible_rect;
1867 win->surface = new_surface;
1868 surface_win = wine_server_ptr_handle( reply->surface_win );
1869 needs_update = reply->needs_update;
1870 if (get_window_long( win->parent, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
1872 RECT client;
1873 get_window_rects( win->parent, COORDS_CLIENT, NULL, &client, get_thread_dpi() );
1874 mirror_rect( &client, &win->window_rect );
1875 mirror_rect( &client, &win->client_rect );
1876 mirror_rect( &client, &win->visible_rect );
1878 /* if an RTL window is resized the children have moved */
1879 if (win->dwExStyle & WS_EX_LAYOUTRTL &&
1880 client_rect->right - client_rect->left != old_client_rect.right - old_client_rect.left)
1881 win->flags |= WIN_CHILDREN_MOVED;
1884 SERVER_END_REQ;
1886 if (ret)
1888 if (needs_update) update_surface_region( surface_win );
1889 if (((swp_flags & SWP_AGG_NOPOSCHANGE) != SWP_AGG_NOPOSCHANGE) ||
1890 (swp_flags & (SWP_HIDEWINDOW | SWP_SHOWWINDOW | SWP_STATECHANGED | SWP_FRAMECHANGED)))
1891 invalidate_dce( win, &old_window_rect );
1894 release_win_ptr( win );
1896 if (ret)
1898 TRACE( "win %p surface %p -> %p\n", hwnd, old_surface, new_surface );
1899 register_window_surface( old_surface, new_surface );
1900 if (old_surface)
1902 if (valid_rects)
1904 move_window_bits( hwnd, old_surface, new_surface, &visible_rect,
1905 &old_visible_rect, window_rect, valid_rects );
1906 valid_rects = NULL; /* prevent the driver from trying to also move the bits */
1908 window_surface_release( old_surface );
1910 else if (surface_win && surface_win != hwnd)
1912 if (valid_rects)
1914 RECT rects[2];
1915 int x_offset = old_visible_rect.left - visible_rect.left;
1916 int y_offset = old_visible_rect.top - visible_rect.top;
1918 /* if all that happened is that the whole window moved, copy everything */
1919 if (!(swp_flags & SWP_FRAMECHANGED) &&
1920 old_visible_rect.right - visible_rect.right == x_offset &&
1921 old_visible_rect.bottom - visible_rect.bottom == y_offset &&
1922 old_client_rect.left - client_rect->left == x_offset &&
1923 old_client_rect.right - client_rect->right == x_offset &&
1924 old_client_rect.top - client_rect->top == y_offset &&
1925 old_client_rect.bottom - client_rect->bottom == y_offset &&
1926 EqualRect( &valid_rects[0], client_rect ))
1928 rects[0] = visible_rect;
1929 rects[1] = old_visible_rect;
1930 valid_rects = rects;
1932 move_window_bits_parent( hwnd, surface_win, window_rect, valid_rects );
1933 valid_rects = NULL; /* prevent the driver from trying to also move the bits */
1937 user_driver->pWindowPosChanged( hwnd, insert_after, swp_flags, window_rect,
1938 client_rect, &visible_rect, valid_rects, new_surface );
1940 else if (new_surface) window_surface_release( new_surface );
1942 return ret;
1945 /*******************************************************************
1946 * NtUserGetWindowRgnEx (win32u.@)
1948 int WINAPI NtUserGetWindowRgnEx( HWND hwnd, HRGN hrgn, UINT unk )
1950 NTSTATUS status;
1951 HRGN win_rgn = 0;
1952 RGNDATA *data;
1953 size_t size = 256;
1954 int ret = ERROR;
1958 if (!(data = malloc( sizeof(*data) + size - 1 )))
1960 RtlSetLastWin32Error( ERROR_OUTOFMEMORY );
1961 return ERROR;
1963 SERVER_START_REQ( get_window_region )
1965 req->window = wine_server_user_handle( hwnd );
1966 wine_server_set_reply( req, data->Buffer, size );
1967 if (!(status = wine_server_call( req )))
1969 size_t reply_size = wine_server_reply_size( reply );
1970 if (reply_size)
1972 data->rdh.dwSize = sizeof(data->rdh);
1973 data->rdh.iType = RDH_RECTANGLES;
1974 data->rdh.nCount = reply_size / sizeof(RECT);
1975 data->rdh.nRgnSize = reply_size;
1976 win_rgn = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1979 else size = reply->total_size;
1981 SERVER_END_REQ;
1982 free( data );
1983 } while (status == STATUS_BUFFER_OVERFLOW);
1985 if (set_ntstatus( status ) && win_rgn)
1987 ret = NtGdiCombineRgn( hrgn, win_rgn, 0, RGN_COPY );
1988 NtGdiDeleteObjectApp( win_rgn );
1990 return ret;
1993 /***********************************************************************
1994 * NtUserSetWindowRgn (win32u.@)
1996 int WINAPI NtUserSetWindowRgn( HWND hwnd, HRGN hrgn, BOOL redraw )
1998 static const RECT empty_rect;
1999 BOOL ret;
2001 if (hrgn)
2003 RGNDATA *data;
2004 DWORD size;
2006 if (!(size = NtGdiGetRegionData( hrgn, 0, NULL ))) return FALSE;
2007 if (!(data = malloc( size ))) return FALSE;
2008 if (!NtGdiGetRegionData( hrgn, size, data ))
2010 free( data );
2011 return FALSE;
2013 SERVER_START_REQ( set_window_region )
2015 req->window = wine_server_user_handle( hwnd );
2016 req->redraw = redraw != 0;
2017 if (data->rdh.nCount)
2018 wine_server_add_data( req, data->Buffer, data->rdh.nCount * sizeof(RECT) );
2019 else
2020 wine_server_add_data( req, &empty_rect, sizeof(empty_rect) );
2021 ret = !wine_server_call_err( req );
2023 SERVER_END_REQ;
2024 free( data );
2026 else /* clear existing region */
2028 SERVER_START_REQ( set_window_region )
2030 req->window = wine_server_user_handle( hwnd );
2031 req->redraw = redraw != 0;
2032 ret = !wine_server_call_err( req );
2034 SERVER_END_REQ;
2037 if (ret)
2039 UINT swp_flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED |
2040 SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE;
2041 if (!redraw) swp_flags |= SWP_NOREDRAW;
2042 user_driver->pSetWindowRgn( hwnd, hrgn, redraw );
2043 NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, swp_flags );
2044 if (hrgn) NtGdiDeleteObjectApp( hrgn );
2046 return ret;
2049 /***********************************************************************
2050 * NtUserMoveWindow (win32u.@)
2052 BOOL WINAPI NtUserMoveWindow( HWND hwnd, INT x, INT y, INT cx, INT cy, BOOL repaint )
2054 int flags = SWP_NOZORDER | SWP_NOACTIVATE;
2055 if (!repaint) flags |= SWP_NOREDRAW;
2056 TRACE( "%p %d,%d %dx%d %d\n", hwnd, x, y, cx, cy, repaint );
2057 return NtUserSetWindowPos( hwnd, 0, x, y, cx, cy, flags );
2060 /*****************************************************************************
2061 * NtUserGetLayeredWindowAttributes (win32u.@)
2063 BOOL WINAPI NtUserGetLayeredWindowAttributes( HWND hwnd, COLORREF *key, BYTE *alpha, DWORD *flags )
2065 BOOL ret;
2067 SERVER_START_REQ( get_window_layered_info )
2069 req->handle = wine_server_user_handle( hwnd );
2070 if ((ret = !wine_server_call_err( req )))
2072 if (key) *key = reply->color_key;
2073 if (alpha) *alpha = reply->alpha;
2074 if (flags) *flags = reply->flags;
2077 SERVER_END_REQ;
2079 return ret;
2082 /*****************************************************************************
2083 * NtUserSetLayeredWindowAttributes (win32u.@)
2085 BOOL WINAPI NtUserSetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha, DWORD flags )
2087 BOOL ret;
2089 TRACE( "(%p,%s,%d,%x)\n", hwnd, debugstr_color(key), alpha, (int)flags );
2091 SERVER_START_REQ( set_window_layered_info )
2093 req->handle = wine_server_user_handle( hwnd );
2094 req->color_key = key;
2095 req->alpha = alpha;
2096 req->flags = flags;
2097 ret = !wine_server_call_err( req );
2099 SERVER_END_REQ;
2101 if (ret)
2103 user_driver->pSetLayeredWindowAttributes( hwnd, key, alpha, flags );
2104 update_window_state( hwnd );
2107 return ret;
2110 /*****************************************************************************
2111 * UpdateLayeredWindow (win32u.@)
2113 BOOL WINAPI NtUserUpdateLayeredWindow( HWND hwnd, HDC hdc_dst, const POINT *pts_dst, const SIZE *size,
2114 HDC hdc_src, const POINT *pts_src, COLORREF key,
2115 const BLENDFUNCTION *blend, DWORD flags, const RECT *dirty )
2117 DWORD swp_flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW;
2118 RECT window_rect, client_rect;
2119 UPDATELAYEREDWINDOWINFO info;
2120 SIZE offset;
2122 if (flags & ~(ULW_COLORKEY | ULW_ALPHA | ULW_OPAQUE | ULW_EX_NORESIZE) ||
2123 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED) ||
2124 NtUserGetLayeredWindowAttributes( hwnd, NULL, NULL, NULL ))
2126 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
2127 return FALSE;
2130 get_window_rects( hwnd, COORDS_PARENT, &window_rect, &client_rect, get_thread_dpi() );
2132 if (pts_dst)
2134 offset.cx = pts_dst->x - window_rect.left;
2135 offset.cy = pts_dst->y - window_rect.top;
2136 OffsetRect( &client_rect, offset.cx, offset.cy );
2137 OffsetRect( &window_rect, offset.cx, offset.cy );
2138 swp_flags &= ~SWP_NOMOVE;
2140 if (size)
2142 offset.cx = size->cx - (window_rect.right - window_rect.left);
2143 offset.cy = size->cy - (window_rect.bottom - window_rect.top);
2144 if (size->cx <= 0 || size->cy <= 0)
2146 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
2147 return FALSE;
2149 if ((flags & ULW_EX_NORESIZE) && (offset.cx || offset.cy))
2151 RtlSetLastWin32Error( ERROR_INCORRECT_SIZE );
2152 return FALSE;
2154 client_rect.right += offset.cx;
2155 client_rect.bottom += offset.cy;
2156 window_rect.right += offset.cx;
2157 window_rect.bottom += offset.cy;
2158 swp_flags &= ~SWP_NOSIZE;
2161 TRACE( "window %p win %s client %s\n", hwnd,
2162 wine_dbgstr_rect(&window_rect), wine_dbgstr_rect(&client_rect) );
2164 apply_window_pos( hwnd, 0, swp_flags, &window_rect, &client_rect, NULL );
2166 info.cbSize = sizeof(info);
2167 info.hdcDst = hdc_dst;
2168 info.pptDst = pts_dst;
2169 info.psize = size;
2170 info.hdcSrc = hdc_src;
2171 info.pptSrc = pts_src;
2172 info.crKey = key;
2173 info.pblend = blend;
2174 info.dwFlags = flags;
2175 info.prcDirty = dirty;
2176 return user_driver->pUpdateLayeredWindow( hwnd, &info, &window_rect );
2179 /***********************************************************************
2180 * list_children_from_point
2182 * Get the list of children that can contain point from the server.
2183 * Point is in screen coordinates.
2184 * Returned list must be freed by caller.
2186 static HWND *list_children_from_point( HWND hwnd, POINT pt )
2188 int i, size = 128;
2189 HWND *list;
2191 for (;;)
2193 int count = 0;
2195 if (!(list = malloc( size * sizeof(HWND) ))) break;
2197 SERVER_START_REQ( get_window_children_from_point )
2199 req->parent = wine_server_user_handle( hwnd );
2200 req->x = pt.x;
2201 req->y = pt.y;
2202 req->dpi = get_thread_dpi();
2203 wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
2204 if (!wine_server_call( req )) count = reply->count;
2206 SERVER_END_REQ;
2207 if (count && count < size)
2209 /* start from the end since HWND is potentially larger than user_handle_t */
2210 for (i = count - 1; i >= 0; i--)
2211 list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
2212 list[count] = 0;
2213 return list;
2215 free( list );
2216 if (!count) break;
2217 size = count + 1; /* restart with a large enough buffer */
2219 return NULL;
2222 /***********************************************************************
2223 * window_from_point
2225 * Find the window and hittest for a given point.
2227 HWND window_from_point( HWND hwnd, POINT pt, INT *hittest )
2229 int i, res;
2230 HWND ret, *list;
2231 POINT win_pt;
2232 int dpi;
2234 if (!hwnd) hwnd = get_desktop_window();
2235 if (!(dpi = get_thread_dpi())) dpi = get_win_monitor_dpi( hwnd );
2237 *hittest = HTNOWHERE;
2239 if (!(list = list_children_from_point( hwnd, pt ))) return 0;
2241 /* now determine the hittest */
2243 for (i = 0; list[i]; i++)
2245 LONG style = get_window_long( list[i], GWL_STYLE );
2247 /* If window is minimized or disabled, return at once */
2248 if (style & WS_DISABLED)
2250 *hittest = HTERROR;
2251 break;
2253 /* Send WM_NCCHITTEST (if same thread) */
2254 if (!is_current_thread_window( list[i] ))
2256 *hittest = HTCLIENT;
2257 break;
2259 win_pt = map_dpi_point( pt, dpi, get_dpi_for_window( list[i] ));
2260 res = send_message( list[i], WM_NCHITTEST, 0, MAKELPARAM( win_pt.x, win_pt.y ));
2261 if (res != HTTRANSPARENT)
2263 *hittest = res; /* Found the window */
2264 break;
2266 /* continue search with next window in z-order */
2268 ret = list[i];
2269 free( list );
2270 TRACE( "scope %p (%d,%d) returning %p\n", hwnd, (int)pt.x, (int)pt.y, ret );
2271 return ret;
2274 /*******************************************************************
2275 * NtUserWindowFromPoint (win32u.@)
2277 HWND WINAPI NtUserWindowFromPoint( LONG x, LONG y )
2279 POINT pt = { .x = x, .y = y };
2280 INT hittest;
2281 return window_from_point( 0, pt, &hittest );
2284 /*******************************************************************
2285 * NtUserChildWindowFromPointEx (win32u.@)
2287 HWND WINAPI NtUserChildWindowFromPointEx( HWND parent, LONG x, LONG y, UINT flags )
2289 POINT pt = { .x = x, .y = y }; /* in the client coordinates */
2290 HWND *list;
2291 int i;
2292 RECT rect;
2293 HWND ret;
2295 get_client_rect( parent, &rect );
2296 if (!PtInRect( &rect, pt )) return 0;
2297 if (!(list = list_window_children( 0, parent, NULL, 0 ))) return parent;
2299 for (i = 0; list[i]; i++)
2301 if (!get_window_rects( list[i], COORDS_PARENT, &rect, NULL, get_thread_dpi() )) continue;
2302 if (!PtInRect( &rect, pt )) continue;
2303 if (flags & (CWP_SKIPINVISIBLE|CWP_SKIPDISABLED))
2305 LONG style = get_window_long( list[i], GWL_STYLE );
2306 if ((flags & CWP_SKIPINVISIBLE) && !(style & WS_VISIBLE)) continue;
2307 if ((flags & CWP_SKIPDISABLED) && (style & WS_DISABLED)) continue;
2309 if (flags & CWP_SKIPTRANSPARENT)
2311 if (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TRANSPARENT) continue;
2313 break;
2315 ret = list[i];
2316 free( list );
2317 if (!ret) ret = parent;
2318 return ret;
2321 /*******************************************************************
2322 * NtUserRealChildWindowFromPoint (win32u.@)
2324 HWND WINAPI NtUserRealChildWindowFromPoint( HWND parent, LONG x, LONG y )
2326 return NtUserChildWindowFromPointEx( parent, x, y, CWP_SKIPTRANSPARENT | CWP_SKIPINVISIBLE );
2329 /*******************************************************************
2330 * get_work_rect
2332 * Get the work area that a maximized window can cover, depending on style.
2334 static BOOL get_work_rect( HWND hwnd, RECT *rect )
2336 HMONITOR monitor = monitor_from_window( hwnd, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
2337 MONITORINFO mon_info;
2338 DWORD style;
2340 if (!monitor) return FALSE;
2342 mon_info.cbSize = sizeof(mon_info);
2343 get_monitor_info( monitor, &mon_info );
2344 *rect = mon_info.rcMonitor;
2346 style = get_window_long( hwnd, GWL_STYLE );
2347 if (style & WS_MAXIMIZEBOX)
2349 if ((style & WS_CAPTION) == WS_CAPTION || !(style & (WS_CHILD | WS_POPUP)))
2350 *rect = mon_info.rcWork;
2352 return TRUE;
2355 static RECT get_maximized_work_rect( HWND hwnd )
2357 RECT work_rect = { 0 };
2359 if ((get_window_long( hwnd, GWL_STYLE ) & (WS_MINIMIZE | WS_MAXIMIZE)) == WS_MAXIMIZE)
2361 if (!get_work_rect( hwnd, &work_rect ))
2362 work_rect = get_primary_monitor_rect( get_thread_dpi() );
2364 return work_rect;
2367 /*******************************************************************
2368 * update_maximized_pos
2370 * For top level windows covering the work area, we might have to
2371 * "forget" the maximized position. Windows presumably does this
2372 * to avoid situations where the border style changes, which would
2373 * lead the window to be outside the screen, or the window gets
2374 * reloaded on a different screen, and the "saved" position no
2375 * longer applies to it (despite being maximized).
2377 * Some applications (e.g. Imperiums: Greek Wars) depend on this.
2379 static void update_maximized_pos( WND *wnd, RECT *work_rect )
2381 if (wnd->parent && wnd->parent != get_desktop_window())
2382 return;
2384 if (wnd->dwStyle & WS_MAXIMIZE)
2386 if (wnd->window_rect.left <= work_rect->left && wnd->window_rect.top <= work_rect->top &&
2387 wnd->window_rect.right >= work_rect->right && wnd->window_rect.bottom >= work_rect->bottom)
2388 wnd->max_pos.x = wnd->max_pos.y = -1;
2390 else
2391 wnd->max_pos.x = wnd->max_pos.y = -1;
2394 static BOOL empty_point( POINT pt )
2396 return pt.x == -1 && pt.y == -1;
2399 /***********************************************************************
2400 * NtUserGetWindowPlacement (win32u.@)
2402 BOOL WINAPI NtUserGetWindowPlacement( HWND hwnd, WINDOWPLACEMENT *placement )
2404 RECT work_rect = get_maximized_work_rect( hwnd );
2405 WND *win = get_win_ptr( hwnd );
2406 UINT win_dpi;
2408 if (!win) return FALSE;
2410 if (win == WND_DESKTOP)
2412 placement->length = sizeof(*placement);
2413 placement->showCmd = SW_SHOWNORMAL;
2414 placement->flags = 0;
2415 placement->ptMinPosition.x = -1;
2416 placement->ptMinPosition.y = -1;
2417 placement->ptMaxPosition.x = -1;
2418 placement->ptMaxPosition.y = -1;
2419 get_window_rect( hwnd, &placement->rcNormalPosition, get_thread_dpi() );
2420 return TRUE;
2422 if (win == WND_OTHER_PROCESS)
2424 RECT normal_position;
2425 DWORD style;
2427 if (!get_window_rect( hwnd, &normal_position, get_thread_dpi() ))
2428 return FALSE;
2430 FIXME("not fully supported on other process window %p.\n", hwnd);
2432 placement->length = sizeof(*placement);
2433 style = get_window_long( hwnd, GWL_STYLE );
2434 if (style & WS_MINIMIZE)
2435 placement->showCmd = SW_SHOWMINIMIZED;
2436 else
2437 placement->showCmd = (style & WS_MAXIMIZE) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL;
2438 /* provide some dummy information */
2439 placement->flags = 0;
2440 placement->ptMinPosition.x = -1;
2441 placement->ptMinPosition.y = -1;
2442 placement->ptMaxPosition.x = -1;
2443 placement->ptMaxPosition.y = -1;
2444 placement->rcNormalPosition = normal_position;
2445 return TRUE;
2448 /* update the placement according to the current style */
2449 if (win->dwStyle & WS_MINIMIZE)
2451 win->min_pos.x = win->window_rect.left;
2452 win->min_pos.y = win->window_rect.top;
2454 else if (win->dwStyle & WS_MAXIMIZE)
2456 win->max_pos.x = win->window_rect.left;
2457 win->max_pos.y = win->window_rect.top;
2459 else
2461 win->normal_rect = win->window_rect;
2463 update_maximized_pos( win, &work_rect );
2465 placement->length = sizeof(*placement);
2466 if (win->dwStyle & WS_MINIMIZE)
2467 placement->showCmd = SW_SHOWMINIMIZED;
2468 else
2469 placement->showCmd = ( win->dwStyle & WS_MAXIMIZE ) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL ;
2470 if (win->flags & WIN_RESTORE_MAX)
2471 placement->flags = WPF_RESTORETOMAXIMIZED;
2472 else
2473 placement->flags = 0;
2474 win_dpi = get_dpi_for_window( hwnd );
2475 placement->ptMinPosition = empty_point(win->min_pos) ? win->min_pos
2476 : map_dpi_point( win->min_pos, win_dpi, get_thread_dpi() );
2477 placement->ptMaxPosition = empty_point(win->max_pos) ? win->max_pos
2478 : map_dpi_point( win->max_pos, win_dpi, get_thread_dpi() );
2479 placement->rcNormalPosition = map_dpi_rect( win->normal_rect, win_dpi, get_thread_dpi() );
2480 release_win_ptr( win );
2482 TRACE( "%p: returning min %d,%d max %d,%d normal %s\n",
2483 hwnd, (int)placement->ptMinPosition.x, (int)placement->ptMinPosition.y,
2484 (int)placement->ptMaxPosition.x, (int)placement->ptMaxPosition.y,
2485 wine_dbgstr_rect(&placement->rcNormalPosition) );
2486 return TRUE;
2489 /* make sure the specified rect is visible on screen */
2490 static void make_rect_onscreen( RECT *rect )
2492 MONITORINFO info;
2493 HMONITOR monitor = monitor_from_rect( rect, MONITOR_DEFAULTTONEAREST, get_thread_dpi() );
2495 info.cbSize = sizeof(info);
2496 if (!monitor || !get_monitor_info( monitor, &info )) return;
2497 /* FIXME: map coordinates from rcWork to rcMonitor */
2498 if (rect->right <= info.rcWork.left)
2500 rect->right += info.rcWork.left - rect->left;
2501 rect->left = info.rcWork.left;
2503 else if (rect->left >= info.rcWork.right)
2505 rect->left += info.rcWork.right - rect->right;
2506 rect->right = info.rcWork.right;
2508 if (rect->bottom <= info.rcWork.top)
2510 rect->bottom += info.rcWork.top - rect->top;
2511 rect->top = info.rcWork.top;
2513 else if (rect->top >= info.rcWork.bottom)
2515 rect->top += info.rcWork.bottom - rect->bottom;
2516 rect->bottom = info.rcWork.bottom;
2520 /***********************************************************************
2521 * NtUserGetInternalWindowPos (win32u.@)
2523 UINT WINAPI NtUserGetInternalWindowPos( HWND hwnd, RECT *rect, POINT *pt )
2525 WINDOWPLACEMENT placement;
2527 placement.length = sizeof(placement);
2528 if (!NtUserGetWindowPlacement( hwnd, &placement )) return 0;
2529 if (rect) *rect = placement.rcNormalPosition;
2530 if (pt) *pt = placement.ptMinPosition;
2531 return placement.showCmd;
2534 /* make sure the specified point is visible on screen */
2535 static void make_point_onscreen( POINT *pt )
2537 RECT rect;
2539 SetRect( &rect, pt->x, pt->y, pt->x + 1, pt->y + 1 );
2540 make_rect_onscreen( &rect );
2541 pt->x = rect.left;
2542 pt->y = rect.top;
2545 static BOOL set_window_placement( HWND hwnd, const WINDOWPLACEMENT *wndpl, UINT flags )
2547 RECT work_rect = get_maximized_work_rect( hwnd );
2548 WND *win = get_win_ptr( hwnd );
2549 WINDOWPLACEMENT wp = *wndpl;
2550 DWORD style;
2552 if (flags & PLACE_MIN) make_point_onscreen( &wp.ptMinPosition );
2553 if (flags & PLACE_MAX) make_point_onscreen( &wp.ptMaxPosition );
2554 if (flags & PLACE_RECT) make_rect_onscreen( &wp.rcNormalPosition );
2556 TRACE( "%p: setting min %d,%d max %d,%d normal %s flags %x adjusted to min %d,%d max %d,%d normal %s\n",
2557 hwnd, (int)wndpl->ptMinPosition.x, (int)wndpl->ptMinPosition.y,
2558 (int)wndpl->ptMaxPosition.x, (int)wndpl->ptMaxPosition.y,
2559 wine_dbgstr_rect(&wndpl->rcNormalPosition), flags,
2560 (int)wp.ptMinPosition.x, (int)wp.ptMinPosition.y,
2561 (int)wp.ptMaxPosition.x, (int)wp.ptMaxPosition.y,
2562 wine_dbgstr_rect(&wp.rcNormalPosition) );
2564 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
2566 if (flags & PLACE_MIN) win->min_pos = point_thread_to_win_dpi( hwnd, wp.ptMinPosition );
2567 if (flags & PLACE_MAX)
2569 win->max_pos = point_thread_to_win_dpi( hwnd, wp.ptMaxPosition );
2570 update_maximized_pos( win, &work_rect );
2572 if (flags & PLACE_RECT) win->normal_rect = rect_thread_to_win_dpi( hwnd, wp.rcNormalPosition );
2574 style = win->dwStyle;
2576 release_win_ptr( win );
2578 if (style & WS_MINIMIZE)
2580 if (flags & PLACE_MIN)
2582 NtUserSetWindowPos( hwnd, 0, wp.ptMinPosition.x, wp.ptMinPosition.y, 0, 0,
2583 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
2586 else if (style & WS_MAXIMIZE)
2588 if (flags & PLACE_MAX)
2589 NtUserSetWindowPos( hwnd, 0, wp.ptMaxPosition.x, wp.ptMaxPosition.y, 0, 0,
2590 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
2592 else if (flags & PLACE_RECT)
2593 NtUserSetWindowPos( hwnd, 0, wp.rcNormalPosition.left, wp.rcNormalPosition.top,
2594 wp.rcNormalPosition.right - wp.rcNormalPosition.left,
2595 wp.rcNormalPosition.bottom - wp.rcNormalPosition.top,
2596 SWP_NOZORDER | SWP_NOACTIVATE );
2598 NtUserShowWindow( hwnd, wndpl->showCmd );
2600 if (is_iconic( hwnd ))
2602 if (wndpl->flags & WPF_RESTORETOMAXIMIZED)
2603 win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
2605 return TRUE;
2608 /***********************************************************************
2609 * NtUserSetWindowPlacement (win32u.@)
2611 BOOL WINAPI NtUserSetWindowPlacement( HWND hwnd, const WINDOWPLACEMENT *wpl )
2613 UINT flags = PLACE_MAX | PLACE_RECT;
2614 if (!wpl) return FALSE;
2615 if (wpl->length != sizeof(*wpl))
2617 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
2618 return FALSE;
2620 if (wpl->flags & WPF_SETMINPOSITION) flags |= PLACE_MIN;
2621 return set_window_placement( hwnd, wpl, flags );
2624 /*****************************************************************************
2625 * NtUserBuildHwndList (win32u.@)
2627 NTSTATUS WINAPI NtUserBuildHwndList( HDESK desktop, ULONG unk2, ULONG unk3, ULONG unk4,
2628 ULONG thread_id, ULONG count, HWND *buffer, ULONG *size )
2630 user_handle_t *list = (user_handle_t *)buffer;
2631 int i;
2632 NTSTATUS status;
2634 SERVER_START_REQ( get_window_children )
2636 req->desktop = wine_server_obj_handle( desktop );
2637 req->tid = thread_id;
2638 if (count) wine_server_set_reply( req, list, (count - 1) * sizeof(user_handle_t) );
2639 status = wine_server_call( req );
2640 if (status && status != STATUS_BUFFER_TOO_SMALL) return status;
2641 *size = reply->count + 1;
2643 SERVER_END_REQ;
2644 if (*size > count) return STATUS_BUFFER_TOO_SMALL;
2646 /* start from the end since HWND is potentially larger than user_handle_t */
2647 for (i = *size - 2; i >= 0; i--)
2648 buffer[i] = wine_server_ptr_handle( list[i] );
2649 buffer[*size - 1] = HWND_BOTTOM;
2650 return STATUS_SUCCESS;
2653 /***********************************************************************
2654 * NtUserFindWindowEx (USER32.@)
2656 HWND WINAPI NtUserFindWindowEx( HWND parent, HWND child, UNICODE_STRING *class, UNICODE_STRING *title,
2657 ULONG unk )
2659 HWND *list;
2660 HWND retvalue = 0;
2661 int i = 0, len = 0, title_len;
2662 WCHAR *buffer = NULL;
2664 if (!parent && child) parent = get_desktop_window();
2665 else if (parent == HWND_MESSAGE) parent = get_hwnd_message_parent();
2667 if (title)
2669 len = title->Length / sizeof(WCHAR) + 1; /* one extra char to check for chars beyond the end */
2670 if (!(buffer = malloc( (len + 1) * sizeof(WCHAR) ))) return 0;
2673 if (!(list = list_window_children( 0, parent, class, 0 ))) goto done;
2675 if (child)
2677 child = get_full_window_handle( child );
2678 while (list[i] && list[i] != child) i++;
2679 if (!list[i]) goto done;
2680 i++; /* start from next window */
2683 if (title)
2685 while (list[i])
2687 title_len = NtUserInternalGetWindowText( list[i], buffer, len + 1 );
2688 if (title_len * sizeof(WCHAR) == title->Length &&
2689 (!title_len || !wcsnicmp( buffer, title->Buffer, title_len )))
2690 break;
2691 i++;
2694 retvalue = list[i];
2696 done:
2697 free( list );
2698 free( buffer );
2699 return retvalue;
2702 /* Retrieve the window text from the server. */
2703 static data_size_t get_server_window_text( HWND hwnd, WCHAR *text, data_size_t count )
2705 data_size_t len = 0, needed = 0;
2707 SERVER_START_REQ( get_window_text )
2709 req->handle = wine_server_user_handle( hwnd );
2710 if (count) wine_server_set_reply( req, text, (count - 1) * sizeof(WCHAR) );
2711 if (!wine_server_call_err( req ))
2713 needed = reply->length;
2714 len = wine_server_reply_size(reply);
2717 SERVER_END_REQ;
2718 if (text) text[len / sizeof(WCHAR)] = 0;
2719 return needed;
2722 /*******************************************************************
2723 * NtUserInternalGetWindowText (win32u.@)
2725 INT WINAPI NtUserInternalGetWindowText( HWND hwnd, WCHAR *text, INT count )
2727 WND *win;
2729 if (count <= 0) return 0;
2730 if (!(win = get_win_ptr( hwnd ))) return 0;
2731 if (win == WND_DESKTOP) text[0] = 0;
2732 else if (win != WND_OTHER_PROCESS)
2734 if (win->text) lstrcpynW( text, win->text, count );
2735 else text[0] = 0;
2736 release_win_ptr( win );
2738 else
2740 get_server_window_text( hwnd, text, count );
2742 return lstrlenW(text);
2745 /*******************************************************************
2746 * get_windows_offset
2748 * Calculate the offset between the origin of the two windows. Used
2749 * to implement MapWindowPoints.
2751 static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, UINT dpi, BOOL *mirrored, POINT *ret_offset )
2753 WND *win;
2754 POINT offset;
2755 BOOL mirror_from, mirror_to, ret;
2756 HWND hwnd;
2758 offset.x = offset.y = 0;
2759 *mirrored = mirror_from = mirror_to = FALSE;
2761 /* Translate source window origin to screen coords */
2762 if (hwnd_from)
2764 if (!(win = get_win_ptr( hwnd_from )))
2766 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2767 return FALSE;
2769 if (win == WND_OTHER_PROCESS) goto other_process;
2770 if (win != WND_DESKTOP)
2772 if (win->dwExStyle & WS_EX_LAYOUTRTL)
2774 mirror_from = TRUE;
2775 offset.x += win->client_rect.right - win->client_rect.left;
2777 while (win->parent)
2779 offset.x += win->client_rect.left;
2780 offset.y += win->client_rect.top;
2781 hwnd = win->parent;
2782 release_win_ptr( win );
2783 if (!(win = get_win_ptr( hwnd ))) break;
2784 if (win == WND_OTHER_PROCESS) goto other_process;
2785 if (win == WND_DESKTOP) break;
2786 if (win->flags & WIN_CHILDREN_MOVED)
2788 release_win_ptr( win );
2789 goto other_process;
2792 if (win && win != WND_DESKTOP) release_win_ptr( win );
2793 offset = map_dpi_point( offset, get_dpi_for_window( hwnd_from ), dpi );
2797 /* Translate origin to destination window coords */
2798 if (hwnd_to)
2800 if (!(win = get_win_ptr( hwnd_to )))
2802 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2803 return FALSE;
2805 if (win == WND_OTHER_PROCESS) goto other_process;
2806 if (win != WND_DESKTOP)
2808 POINT pt = { 0, 0 };
2809 if (win->dwExStyle & WS_EX_LAYOUTRTL)
2811 mirror_to = TRUE;
2812 pt.x += win->client_rect.right - win->client_rect.left;
2814 while (win->parent)
2816 pt.x += win->client_rect.left;
2817 pt.y += win->client_rect.top;
2818 hwnd = win->parent;
2819 release_win_ptr( win );
2820 if (!(win = get_win_ptr( hwnd ))) break;
2821 if (win == WND_OTHER_PROCESS) goto other_process;
2822 if (win == WND_DESKTOP) break;
2823 if (win->flags & WIN_CHILDREN_MOVED)
2825 release_win_ptr( win );
2826 goto other_process;
2829 if (win && win != WND_DESKTOP) release_win_ptr( win );
2830 pt = map_dpi_point( pt, get_dpi_for_window( hwnd_to ), dpi );
2831 offset.x -= pt.x;
2832 offset.y -= pt.y;
2836 *mirrored = mirror_from ^ mirror_to;
2837 if (mirror_from) offset.x = -offset.x;
2838 *ret_offset = offset;
2839 return TRUE;
2841 other_process: /* one of the parents may belong to another process, do it the hard way */
2842 SERVER_START_REQ( get_windows_offset )
2844 req->from = wine_server_user_handle( hwnd_from );
2845 req->to = wine_server_user_handle( hwnd_to );
2846 req->dpi = dpi;
2847 if ((ret = !wine_server_call_err( req )))
2849 ret_offset->x = reply->x;
2850 ret_offset->y = reply->y;
2851 *mirrored = reply->mirror;
2854 SERVER_END_REQ;
2855 return ret;
2858 /* see ClientToScreen */
2859 BOOL client_to_screen( HWND hwnd, POINT *pt )
2861 POINT offset;
2862 BOOL mirrored;
2864 if (!hwnd)
2866 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2867 return FALSE;
2870 if (!get_windows_offset( hwnd, 0, get_thread_dpi(), &mirrored, &offset )) return FALSE;
2871 pt->x += offset.x;
2872 pt->y += offset.y;
2873 if (mirrored) pt->x = -pt->x;
2874 return TRUE;
2877 /* see ScreenToClient */
2878 BOOL screen_to_client( HWND hwnd, POINT *pt )
2880 POINT offset;
2881 BOOL mirrored;
2883 if (!hwnd)
2885 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2886 return FALSE;
2888 if (!get_windows_offset( 0, hwnd, get_thread_dpi(), &mirrored, &offset )) return FALSE;
2889 pt->x += offset.x;
2890 pt->y += offset.y;
2891 if (mirrored) pt->x = -pt->x;
2892 return TRUE;
2895 /* map coordinates of a window region */
2896 void map_window_region( HWND from, HWND to, HRGN hrgn )
2898 BOOL mirrored;
2899 POINT offset;
2900 UINT i, size;
2901 RGNDATA *data;
2902 HRGN new_rgn;
2903 RECT *rect;
2905 if (!get_windows_offset( from, to, get_thread_dpi(), &mirrored, &offset )) return;
2907 if (!mirrored)
2909 NtGdiOffsetRgn( hrgn, offset.x, offset.y );
2910 return;
2912 if (!(size = NtGdiGetRegionData( hrgn, 0, NULL ))) return;
2913 if (!(data = malloc( size ))) return;
2914 NtGdiGetRegionData( hrgn, size, data );
2915 rect = (RECT *)data->Buffer;
2916 for (i = 0; i < data->rdh.nCount; i++)
2918 int tmp = -(rect[i].left + offset.x);
2919 rect[i].left = -(rect[i].right + offset.x);
2920 rect[i].right = tmp;
2921 rect[i].top += offset.y;
2922 rect[i].bottom += offset.y;
2924 if ((new_rgn = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data )))
2926 NtGdiCombineRgn( hrgn, new_rgn, 0, RGN_COPY );
2927 NtGdiDeleteObjectApp( new_rgn );
2929 free( data );
2932 /* see MapWindowPoints */
2933 int map_window_points( HWND hwnd_from, HWND hwnd_to, POINT *points, UINT count, UINT dpi )
2935 BOOL mirrored;
2936 POINT offset;
2937 UINT i;
2939 if (!get_windows_offset( hwnd_from, hwnd_to, dpi, &mirrored, &offset )) return 0;
2941 for (i = 0; i < count; i++)
2943 points[i].x += offset.x;
2944 points[i].y += offset.y;
2945 if (mirrored) points[i].x = -points[i].x;
2947 if (mirrored && count == 2) /* special case for rectangle */
2949 int tmp = points[0].x;
2950 points[0].x = points[1].x;
2951 points[1].x = tmp;
2953 return MAKELONG( LOWORD(offset.x), LOWORD(offset.y) );
2956 /***********************************************************************
2957 * dump_winpos_flags
2959 static void dump_winpos_flags( UINT flags )
2961 static const UINT dumped_flags = (SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW |
2962 SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_SHOWWINDOW |
2963 SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOOWNERZORDER |
2964 SWP_NOSENDCHANGING | SWP_DEFERERASE | SWP_ASYNCWINDOWPOS |
2965 SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE | SWP_STATECHANGED);
2966 TRACE( "flags:" );
2967 if(flags & SWP_NOSIZE) TRACE( " SWP_NOSIZE" );
2968 if(flags & SWP_NOMOVE) TRACE( " SWP_NOMOVE" );
2969 if(flags & SWP_NOZORDER) TRACE( " SWP_NOZORDER" );
2970 if(flags & SWP_NOREDRAW) TRACE( " SWP_NOREDRAW" );
2971 if(flags & SWP_NOACTIVATE) TRACE( " SWP_NOACTIVATE" );
2972 if(flags & SWP_FRAMECHANGED) TRACE( " SWP_FRAMECHANGED" );
2973 if(flags & SWP_SHOWWINDOW) TRACE( " SWP_SHOWWINDOW" );
2974 if(flags & SWP_HIDEWINDOW) TRACE( " SWP_HIDEWINDOW" );
2975 if(flags & SWP_NOCOPYBITS) TRACE( " SWP_NOCOPYBITS" );
2976 if(flags & SWP_NOOWNERZORDER) TRACE( " SWP_NOOWNERZORDER" );
2977 if(flags & SWP_NOSENDCHANGING) TRACE( " SWP_NOSENDCHANGING" );
2978 if(flags & SWP_DEFERERASE) TRACE( " SWP_DEFERERASE" );
2979 if(flags & SWP_ASYNCWINDOWPOS) TRACE( " SWP_ASYNCWINDOWPOS" );
2980 if(flags & SWP_NOCLIENTSIZE) TRACE( " SWP_NOCLIENTSIZE" );
2981 if(flags & SWP_NOCLIENTMOVE) TRACE( " SWP_NOCLIENTMOVE" );
2982 if(flags & SWP_STATECHANGED) TRACE( " SWP_STATECHANGED" );
2984 if(flags & ~dumped_flags) TRACE( " %08x", flags & ~dumped_flags );
2985 TRACE( "\n" );
2988 /***********************************************************************
2989 * map_dpi_winpos
2991 static void map_dpi_winpos( WINDOWPOS *winpos )
2993 UINT dpi_from = get_thread_dpi();
2994 UINT dpi_to = get_dpi_for_window( winpos->hwnd );
2996 if (!dpi_from) dpi_from = get_win_monitor_dpi( winpos->hwnd );
2997 if (dpi_from == dpi_to) return;
2998 winpos->x = muldiv( winpos->x, dpi_to, dpi_from );
2999 winpos->y = muldiv( winpos->y, dpi_to, dpi_from );
3000 winpos->cx = muldiv( winpos->cx, dpi_to, dpi_from );
3001 winpos->cy = muldiv( winpos->cy, dpi_to, dpi_from );
3004 /***********************************************************************
3005 * calc_winpos
3007 static BOOL calc_winpos( WINDOWPOS *winpos, RECT *old_window_rect, RECT *old_client_rect,
3008 RECT *new_window_rect, RECT *new_client_rect )
3010 WND *win;
3012 /* Send WM_WINDOWPOSCHANGING message */
3013 if (!(winpos->flags & SWP_NOSENDCHANGING)
3014 && !((winpos->flags & SWP_AGG_NOCLIENTCHANGE) && (winpos->flags & SWP_SHOWWINDOW)))
3015 send_message( winpos->hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)winpos );
3017 if (!(win = get_win_ptr( winpos->hwnd )) ||
3018 win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
3020 /* Calculate new position and size */
3021 get_window_rects( winpos->hwnd, COORDS_PARENT, old_window_rect, old_client_rect, get_thread_dpi() );
3022 *new_window_rect = *old_window_rect;
3023 *new_client_rect = *old_client_rect;
3025 if (!(winpos->flags & SWP_NOSIZE))
3027 if (win->dwStyle & WS_MINIMIZE)
3029 new_window_rect->right = new_window_rect->left + get_system_metrics( SM_CXMINIMIZED );
3030 new_window_rect->bottom = new_window_rect->top + get_system_metrics( SM_CYMINIMIZED );
3032 else
3034 new_window_rect->right = new_window_rect->left + winpos->cx;
3035 new_window_rect->bottom = new_window_rect->top + winpos->cy;
3039 if (!(winpos->flags & SWP_NOMOVE))
3041 /* If the window is toplevel minimized off-screen, force keep it there */
3042 if ((win->dwStyle & WS_MINIMIZE) &&
3043 win->window_rect.left <= -32000 && win->window_rect.top <= -32000 &&
3044 (!win->parent || win->parent == get_desktop_window()))
3046 winpos->x = -32000;
3047 winpos->y = -32000;
3049 new_window_rect->left = winpos->x;
3050 new_window_rect->top = winpos->y;
3051 new_window_rect->right += winpos->x - old_window_rect->left;
3052 new_window_rect->bottom += winpos->y - old_window_rect->top;
3054 OffsetRect( new_client_rect, winpos->x - old_window_rect->left,
3055 winpos->y - old_window_rect->top );
3057 winpos->flags |= SWP_NOCLIENTMOVE | SWP_NOCLIENTSIZE;
3059 TRACE( "hwnd %p, after %p, swp %d,%d %dx%d flags %08x current %s style %08x new %s\n",
3060 winpos->hwnd, winpos->hwndInsertAfter, winpos->x, winpos->y,
3061 winpos->cx, winpos->cy, winpos->flags,
3062 wine_dbgstr_rect( old_window_rect ), win->dwStyle,
3063 wine_dbgstr_rect( new_window_rect ));
3065 release_win_ptr( win );
3066 return TRUE;
3069 /***********************************************************************
3070 * get_valid_rects
3072 * Compute the valid rects from the old and new client rect and WVR_* flags.
3073 * Helper for WM_NCCALCSIZE handling.
3075 static inline void get_valid_rects( const RECT *old_client, const RECT *new_client, UINT flags,
3076 RECT *valid )
3078 int cx, cy;
3080 if (flags & WVR_REDRAW)
3082 SetRectEmpty( &valid[0] );
3083 SetRectEmpty( &valid[1] );
3084 return;
3087 if (flags & WVR_VALIDRECTS)
3089 if (!intersect_rect( &valid[0], &valid[0], new_client ) ||
3090 !intersect_rect( &valid[1], &valid[1], old_client ))
3092 SetRectEmpty( &valid[0] );
3093 SetRectEmpty( &valid[1] );
3094 return;
3096 flags = WVR_ALIGNLEFT | WVR_ALIGNTOP;
3098 else
3100 valid[0] = *new_client;
3101 valid[1] = *old_client;
3104 /* make sure the rectangles have the same size */
3105 cx = min( valid[0].right - valid[0].left, valid[1].right - valid[1].left );
3106 cy = min( valid[0].bottom - valid[0].top, valid[1].bottom - valid[1].top );
3108 if (flags & WVR_ALIGNBOTTOM)
3110 valid[0].top = valid[0].bottom - cy;
3111 valid[1].top = valid[1].bottom - cy;
3113 else
3115 valid[0].bottom = valid[0].top + cy;
3116 valid[1].bottom = valid[1].top + cy;
3118 if (flags & WVR_ALIGNRIGHT)
3120 valid[0].left = valid[0].right - cx;
3121 valid[1].left = valid[1].right - cx;
3123 else
3125 valid[0].right = valid[0].left + cx;
3126 valid[1].right = valid[1].left + cx;
3130 static UINT calc_ncsize( WINDOWPOS *winpos, const RECT *old_window_rect, const RECT *old_client_rect,
3131 const RECT *new_window_rect, RECT *new_client_rect, RECT *valid_rects,
3132 int parent_x, int parent_y )
3134 UINT wvr_flags = 0;
3136 /* Send WM_NCCALCSIZE message to get new client area */
3137 if ((winpos->flags & (SWP_FRAMECHANGED | SWP_NOSIZE)) != SWP_NOSIZE)
3139 NCCALCSIZE_PARAMS params;
3140 WINDOWPOS winposCopy;
3141 UINT class_style;
3143 params.rgrc[0] = *new_window_rect;
3144 params.rgrc[1] = *old_window_rect;
3145 params.rgrc[2] = *old_client_rect;
3146 params.lppos = &winposCopy;
3147 winposCopy = *winpos;
3149 if (winpos->flags & SWP_NOMOVE)
3151 winposCopy.x = old_window_rect->left;
3152 winposCopy.y = old_window_rect->top;
3155 if (winpos->flags & SWP_NOSIZE)
3157 winposCopy.cx = old_window_rect->right - old_window_rect->left;
3158 winposCopy.cy = old_window_rect->bottom - old_window_rect->top;
3161 class_style = get_class_long( winpos->hwnd, GCL_STYLE, FALSE );
3162 if (class_style & CS_VREDRAW) wvr_flags |= WVR_VREDRAW;
3163 if (class_style & CS_HREDRAW) wvr_flags |= WVR_HREDRAW;
3165 wvr_flags |= send_message( winpos->hwnd, WM_NCCALCSIZE, TRUE, (LPARAM)&params );
3167 *new_client_rect = params.rgrc[0];
3169 TRACE( "hwnd %p old win %s old client %s new win %s new client %s\n", winpos->hwnd,
3170 wine_dbgstr_rect(old_window_rect), wine_dbgstr_rect(old_client_rect),
3171 wine_dbgstr_rect(new_window_rect), wine_dbgstr_rect(new_client_rect) );
3173 if (new_client_rect->left != old_client_rect->left - parent_x ||
3174 new_client_rect->top != old_client_rect->top - parent_y)
3175 winpos->flags &= ~SWP_NOCLIENTMOVE;
3177 if ((new_client_rect->right - new_client_rect->left !=
3178 old_client_rect->right - old_client_rect->left))
3179 winpos->flags &= ~SWP_NOCLIENTSIZE;
3180 else
3181 wvr_flags &= ~WVR_HREDRAW;
3183 if (new_client_rect->bottom - new_client_rect->top !=
3184 old_client_rect->bottom - old_client_rect->top)
3185 winpos->flags &= ~SWP_NOCLIENTSIZE;
3186 else
3187 wvr_flags &= ~WVR_VREDRAW;
3189 valid_rects[0] = params.rgrc[1];
3190 valid_rects[1] = params.rgrc[2];
3192 else
3194 if (!(winpos->flags & SWP_NOMOVE) &&
3195 (new_client_rect->left != old_client_rect->left - parent_x ||
3196 new_client_rect->top != old_client_rect->top - parent_y))
3197 winpos->flags &= ~SWP_NOCLIENTMOVE;
3200 if (winpos->flags & (SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_SHOWWINDOW | SWP_HIDEWINDOW))
3202 SetRectEmpty( &valid_rects[0] );
3203 SetRectEmpty( &valid_rects[1] );
3205 else get_valid_rects( old_client_rect, new_client_rect, wvr_flags, valid_rects );
3207 return wvr_flags;
3210 /* fix redundant flags and values in the WINDOWPOS structure */
3211 static BOOL fixup_swp_flags( WINDOWPOS *winpos, const RECT *old_window_rect, int parent_x, int parent_y )
3213 HWND parent;
3214 WND *win = get_win_ptr( winpos->hwnd );
3215 BOOL ret = TRUE;
3217 if (!win || win == WND_OTHER_PROCESS)
3219 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
3220 return FALSE;
3222 winpos->hwnd = win->obj.handle; /* make it a full handle */
3224 /* Finally make sure that all coordinates are valid */
3225 if (winpos->x < -32768) winpos->x = -32768;
3226 else if (winpos->x > 32767) winpos->x = 32767;
3227 if (winpos->y < -32768) winpos->y = -32768;
3228 else if (winpos->y > 32767) winpos->y = 32767;
3230 if (winpos->cx < 0) winpos->cx = 0;
3231 else if (winpos->cx > 32767) winpos->cx = 32767;
3232 if (winpos->cy < 0) winpos->cy = 0;
3233 else if (winpos->cy > 32767) winpos->cy = 32767;
3235 parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3236 if (!is_window_visible( parent )) winpos->flags |= SWP_NOREDRAW;
3238 if (win->dwStyle & WS_VISIBLE) winpos->flags &= ~SWP_SHOWWINDOW;
3239 else
3241 winpos->flags &= ~SWP_HIDEWINDOW;
3242 if (!(winpos->flags & SWP_SHOWWINDOW)) winpos->flags |= SWP_NOREDRAW;
3245 if ((old_window_rect->right - old_window_rect->left == winpos->cx) &&
3246 (old_window_rect->bottom - old_window_rect->top == winpos->cy))
3247 winpos->flags |= SWP_NOSIZE; /* Already the right size */
3249 if ((old_window_rect->left - parent_x == winpos->x) && (old_window_rect->top - parent_y == winpos->y))
3250 winpos->flags |= SWP_NOMOVE; /* Already the right position */
3252 if ((win->dwStyle & (WS_POPUP | WS_CHILD)) != WS_CHILD)
3254 if (!(winpos->flags & (SWP_NOACTIVATE|SWP_HIDEWINDOW)) && /* Bring to the top when activating */
3255 (winpos->flags & SWP_NOZORDER ||
3256 (winpos->hwndInsertAfter != HWND_TOPMOST && winpos->hwndInsertAfter != HWND_NOTOPMOST)))
3258 winpos->flags &= ~SWP_NOZORDER;
3259 winpos->hwndInsertAfter = HWND_TOP;
3263 /* Check hwndInsertAfter */
3264 if (winpos->flags & SWP_NOZORDER) goto done;
3266 if (winpos->hwndInsertAfter == HWND_TOP)
3268 if (get_window_relative( winpos->hwnd, GW_HWNDFIRST ) == winpos->hwnd)
3269 winpos->flags |= SWP_NOZORDER;
3271 else if (winpos->hwndInsertAfter == HWND_BOTTOM)
3273 if (!(win->dwExStyle & WS_EX_TOPMOST) &&
3274 get_window_relative( winpos->hwnd, GW_HWNDLAST ) == winpos->hwnd)
3275 winpos->flags |= SWP_NOZORDER;
3277 else if (winpos->hwndInsertAfter == HWND_TOPMOST)
3279 if ((win->dwExStyle & WS_EX_TOPMOST) &&
3280 get_window_relative( winpos->hwnd, GW_HWNDFIRST ) == winpos->hwnd)
3281 winpos->flags |= SWP_NOZORDER;
3283 else if (winpos->hwndInsertAfter == HWND_NOTOPMOST)
3285 if (!(win->dwExStyle & WS_EX_TOPMOST))
3286 winpos->flags |= SWP_NOZORDER;
3288 else
3290 if ((winpos->hwnd == winpos->hwndInsertAfter) ||
3291 (winpos->hwnd == get_window_relative( winpos->hwndInsertAfter, GW_HWNDNEXT )))
3292 winpos->flags |= SWP_NOZORDER;
3294 done:
3295 release_win_ptr( win );
3296 return ret;
3299 /***********************************************************************
3300 * swp_owner_popups
3302 * fix Z order taking into account owned popups -
3303 * basically we need to maintain them above the window that owns them
3305 * FIXME: hide/show owned popups when owner visibility changes.
3307 static HWND swp_owner_popups( HWND hwnd, HWND after )
3309 HWND owner, *list = NULL;
3310 unsigned int i;
3312 TRACE( "(%p) after = %p\n", hwnd, after );
3314 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) return after;
3316 if ((owner = get_window_relative( hwnd, GW_OWNER )))
3318 /* make sure this popup stays above the owner */
3320 if (after != HWND_TOPMOST)
3322 if (!(list = list_window_children( 0, get_desktop_window(), NULL, 0 ))) return after;
3324 for (i = 0; list[i]; i++)
3326 BOOL topmost = (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TOPMOST) != 0;
3328 if (list[i] == owner)
3330 if (i > 0) after = list[i-1];
3331 else after = topmost ? HWND_TOPMOST : HWND_TOP;
3332 break;
3335 if (after == HWND_TOP || after == HWND_NOTOPMOST)
3337 if (!topmost) break;
3339 else if (list[i] == after) break;
3344 if (after == HWND_BOTTOM) goto done;
3345 if (!list && !(list = list_window_children( 0, get_desktop_window(), NULL, 0 ))) goto done;
3347 i = 0;
3348 if (after == HWND_TOP || after == HWND_NOTOPMOST)
3350 if (after == HWND_NOTOPMOST ||
3351 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST))
3353 /* skip all the topmost windows */
3354 while (list[i] && (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TOPMOST)) i++;
3357 else if (after != HWND_TOPMOST)
3359 /* skip windows that are already placed correctly */
3360 for (i = 0; list[i]; i++)
3362 if (list[i] == after) break;
3363 if (list[i] == hwnd) goto done; /* nothing to do if window is moving backwards in z-order */
3367 for ( ; list[i]; i++)
3369 if (list[i] == hwnd) break;
3370 if (get_window_relative( list[i], GW_OWNER ) != hwnd) continue;
3371 TRACE( "moving %p owned by %p after %p\n", list[i], hwnd, after );
3372 NtUserSetWindowPos( list[i], after, 0, 0, 0, 0,
3373 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_DEFERERASE );
3374 after = list[i];
3377 done:
3378 free( list );
3379 return after;
3382 /* NtUserSetWindowPos implementation */
3383 BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y )
3385 RECT old_window_rect, old_client_rect, new_window_rect, new_client_rect, valid_rects[2];
3386 UINT orig_flags;
3387 BOOL ret = FALSE;
3388 DPI_AWARENESS_CONTEXT context;
3390 orig_flags = winpos->flags;
3392 /* First, check z-order arguments. */
3393 if (!(winpos->flags & SWP_NOZORDER))
3395 /* fix sign extension */
3396 if (winpos->hwndInsertAfter == (HWND)0xffff) winpos->hwndInsertAfter = HWND_TOPMOST;
3397 else if (winpos->hwndInsertAfter == (HWND)0xfffe) winpos->hwndInsertAfter = HWND_NOTOPMOST;
3399 if (!(winpos->hwndInsertAfter == HWND_TOP ||
3400 winpos->hwndInsertAfter == HWND_BOTTOM ||
3401 winpos->hwndInsertAfter == HWND_TOPMOST ||
3402 winpos->hwndInsertAfter == HWND_NOTOPMOST))
3404 HWND parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3405 HWND insertafter_parent = NtUserGetAncestor( winpos->hwndInsertAfter, GA_PARENT );
3407 /* hwndInsertAfter must be a sibling of the window */
3408 if (!insertafter_parent) return FALSE;
3409 if (insertafter_parent != parent) return TRUE;
3413 /* Make sure that coordinates are valid for WM_WINDOWPOSCHANGING */
3414 if (!(winpos->flags & SWP_NOMOVE))
3416 if (winpos->x < -32768) winpos->x = -32768;
3417 else if (winpos->x > 32767) winpos->x = 32767;
3418 if (winpos->y < -32768) winpos->y = -32768;
3419 else if (winpos->y > 32767) winpos->y = 32767;
3421 if (!(winpos->flags & SWP_NOSIZE))
3423 if (winpos->cx < 0) winpos->cx = 0;
3424 else if (winpos->cx > 32767) winpos->cx = 32767;
3425 if (winpos->cy < 0) winpos->cy = 0;
3426 else if (winpos->cy > 32767) winpos->cy = 32767;
3429 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( winpos->hwnd ));
3431 if (!calc_winpos( winpos, &old_window_rect, &old_client_rect,
3432 &new_window_rect, &new_client_rect )) goto done;
3434 /* Fix redundant flags */
3435 if (!fixup_swp_flags( winpos, &old_window_rect, parent_x, parent_y )) goto done;
3437 if((winpos->flags & (SWP_NOZORDER | SWP_HIDEWINDOW | SWP_SHOWWINDOW)) != SWP_NOZORDER)
3439 if (NtUserGetAncestor( winpos->hwnd, GA_PARENT ) == get_desktop_window())
3440 winpos->hwndInsertAfter = swp_owner_popups( winpos->hwnd, winpos->hwndInsertAfter );
3443 /* Common operations */
3445 calc_ncsize( winpos, &old_window_rect, &old_client_rect,
3446 &new_window_rect, &new_client_rect, valid_rects, parent_x, parent_y );
3448 if (!apply_window_pos( winpos->hwnd, winpos->hwndInsertAfter, winpos->flags,
3449 &new_window_rect, &new_client_rect, valid_rects ))
3450 goto done;
3452 if (winpos->flags & SWP_HIDEWINDOW)
3454 NtUserNotifyWinEvent( EVENT_OBJECT_HIDE, winpos->hwnd, 0, 0 );
3456 NtUserHideCaret( winpos->hwnd );
3458 else if (winpos->flags & SWP_SHOWWINDOW)
3460 NtUserNotifyWinEvent( EVENT_OBJECT_SHOW, winpos->hwnd, 0, 0 );
3462 NtUserShowCaret( winpos->hwnd );
3465 if (!(winpos->flags & (SWP_NOACTIVATE|SWP_HIDEWINDOW)))
3467 /* child windows get WM_CHILDACTIVATE message */
3468 if ((get_window_long( winpos->hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD)
3469 send_message( winpos->hwnd, WM_CHILDACTIVATE, 0, 0 );
3470 else
3471 set_foreground_window( winpos->hwnd, FALSE );
3474 if(!(orig_flags & SWP_DEFERERASE))
3476 /* erase parent when hiding or resizing child */
3477 if ((orig_flags & SWP_HIDEWINDOW) ||
3478 (!(orig_flags & SWP_SHOWWINDOW) &&
3479 (winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOGEOMETRYCHANGE))
3481 HWND parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3482 if (!parent || parent == get_desktop_window()) parent = winpos->hwnd;
3483 erase_now( parent, 0 );
3486 /* Give newly shown windows a chance to redraw */
3487 if(((winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOPOSCHANGE)
3488 && !(orig_flags & SWP_AGG_NOCLIENTCHANGE) && (orig_flags & SWP_SHOWWINDOW))
3490 erase_now(winpos->hwnd, 0);
3494 /* And last, send the WM_WINDOWPOSCHANGED message */
3496 TRACE( "\tstatus flags = %04x\n", winpos->flags & SWP_AGG_STATUSFLAGS );
3498 if (((winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOPOSCHANGE)
3499 && !((orig_flags & SWP_AGG_NOCLIENTCHANGE) && (orig_flags & SWP_SHOWWINDOW)))
3501 /* WM_WINDOWPOSCHANGED is sent even if SWP_NOSENDCHANGING is set
3502 and always contains final window position.
3504 winpos->x = new_window_rect.left;
3505 winpos->y = new_window_rect.top;
3506 winpos->cx = new_window_rect.right - new_window_rect.left;
3507 winpos->cy = new_window_rect.bottom - new_window_rect.top;
3508 send_message( winpos->hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)winpos );
3510 ret = TRUE;
3511 done:
3512 SetThreadDpiAwarenessContext( context );
3513 return ret;
3516 /*******************************************************************
3517 * NtUserSetWindowPos (win32u.@)
3519 BOOL WINAPI NtUserSetWindowPos( HWND hwnd, HWND after, INT x, INT y, INT cx, INT cy, UINT flags )
3521 WINDOWPOS winpos;
3523 TRACE( "hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n", hwnd, after, x, y, cx, cy, flags );
3524 if(TRACE_ON(win)) dump_winpos_flags(flags);
3526 if (is_broadcast( hwnd ))
3528 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
3529 return FALSE;
3532 winpos.hwnd = get_full_window_handle( hwnd );
3533 winpos.hwndInsertAfter = get_full_window_handle( after );
3534 winpos.x = x;
3535 winpos.y = y;
3536 winpos.cx = cx;
3537 winpos.cy = cy;
3538 winpos.flags = flags;
3540 map_dpi_winpos( &winpos );
3542 if (is_current_thread_window( hwnd ))
3543 return set_window_pos( &winpos, 0, 0 );
3545 if (flags & SWP_ASYNCWINDOWPOS)
3546 return NtUserMessageCall( winpos.hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)&winpos,
3547 0, NtUserSendNotifyMessage, FALSE );
3548 else
3549 return send_message( winpos.hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)&winpos );
3552 typedef struct
3554 struct user_object obj;
3555 INT count;
3556 INT suggested_count;
3557 HWND parent;
3558 WINDOWPOS *winpos;
3559 } DWP;
3561 /* see BeginDeferWindowPos */
3562 HDWP begin_defer_window_pos( INT count )
3564 HDWP handle = 0;
3565 DWP *dwp;
3567 TRACE( "%d\n", count );
3569 if (count < 0)
3571 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
3572 return 0;
3574 /* Windows allows zero count, in which case it allocates context for 8 moves */
3575 if (count == 0) count = 8;
3577 if (!(dwp = malloc( sizeof(DWP) ))) return 0;
3579 dwp->count = 0;
3580 dwp->parent = 0;
3581 dwp->suggested_count = count;
3583 if (!(dwp->winpos = malloc( count * sizeof(WINDOWPOS) )) ||
3584 !(handle = alloc_user_handle( &dwp->obj, NTUSER_OBJ_WINPOS )))
3586 free( dwp->winpos );
3587 free( dwp );
3590 TRACE( "returning %p\n", handle );
3591 return handle;
3594 /***********************************************************************
3595 * NtUserDeferWindowPosAndBand (win32u.@)
3597 HDWP WINAPI NtUserDeferWindowPosAndBand( HDWP hdwp, HWND hwnd, HWND after,
3598 INT x, INT y, INT cx, INT cy,
3599 UINT flags, UINT unk1, UINT unk2 )
3601 HDWP retvalue = hdwp;
3602 WINDOWPOS winpos;
3603 DWP *dwp;
3604 int i;
3606 TRACE( "hdwp %p, hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n",
3607 hdwp, hwnd, after, x, y, cx, cy, flags );
3609 winpos.hwnd = get_full_window_handle( hwnd );
3610 if (is_desktop_window( winpos.hwnd ) || !is_window( winpos.hwnd ))
3612 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
3613 return 0;
3616 winpos.hwndInsertAfter = get_full_window_handle( after );
3617 winpos.flags = flags;
3618 winpos.x = x;
3619 winpos.y = y;
3620 winpos.cx = cx;
3621 winpos.cy = cy;
3622 map_dpi_winpos( &winpos );
3624 if (!(dwp = get_user_handle_ptr( hdwp, NTUSER_OBJ_WINPOS ))) return 0;
3625 if (dwp == OBJ_OTHER_PROCESS)
3627 FIXME( "other process handle %p\n", hdwp );
3628 return 0;
3631 for (i = 0; i < dwp->count; i++)
3633 if (dwp->winpos[i].hwnd == winpos.hwnd)
3635 /* Merge with the other changes */
3636 if (!(flags & SWP_NOZORDER))
3638 dwp->winpos[i].hwndInsertAfter = winpos.hwndInsertAfter;
3640 if (!(flags & SWP_NOMOVE))
3642 dwp->winpos[i].x = winpos.x;
3643 dwp->winpos[i].y = winpos.y;
3645 if (!(flags & SWP_NOSIZE))
3647 dwp->winpos[i].cx = winpos.cx;
3648 dwp->winpos[i].cy = winpos.cy;
3650 dwp->winpos[i].flags &= flags | ~(SWP_NOSIZE | SWP_NOMOVE |
3651 SWP_NOZORDER | SWP_NOREDRAW |
3652 SWP_NOACTIVATE | SWP_NOCOPYBITS|
3653 SWP_NOOWNERZORDER);
3654 dwp->winpos[i].flags |= flags & (SWP_SHOWWINDOW | SWP_HIDEWINDOW |
3655 SWP_FRAMECHANGED);
3656 goto done;
3659 if (dwp->count >= dwp->suggested_count)
3661 WINDOWPOS *newpos = realloc( dwp->winpos, dwp->suggested_count * 2 * sizeof(WINDOWPOS) );
3662 if (!newpos)
3664 retvalue = 0;
3665 goto done;
3667 dwp->suggested_count *= 2;
3668 dwp->winpos = newpos;
3670 dwp->winpos[dwp->count++] = winpos;
3671 done:
3672 release_user_handle_ptr( dwp );
3673 return retvalue;
3676 /***********************************************************************
3677 * NtUserEndDeferWindowPosEx (win32u.@)
3679 BOOL WINAPI NtUserEndDeferWindowPosEx( HDWP hdwp, BOOL async )
3681 WINDOWPOS *winpos;
3682 DWP *dwp;
3683 int i;
3685 TRACE( "%p\n", hdwp );
3687 if (async) FIXME( "async not supported\n" );
3689 if (!(dwp = free_user_handle( hdwp, NTUSER_OBJ_WINPOS ))) return FALSE;
3690 if (dwp == OBJ_OTHER_PROCESS)
3692 FIXME( "other process handle %p\n", hdwp );
3693 return FALSE;
3696 for (i = 0, winpos = dwp->winpos; i < dwp->count; i++, winpos++)
3698 TRACE( "hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n",
3699 winpos->hwnd, winpos->hwndInsertAfter, winpos->x, winpos->y,
3700 winpos->cx, winpos->cy, winpos->flags );
3702 if (is_current_thread_window( winpos->hwnd ))
3703 set_window_pos( winpos, 0, 0 );
3704 else
3705 send_message( winpos->hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)winpos );
3707 free( dwp->winpos );
3708 free( dwp );
3709 return TRUE;
3712 /***********************************************************************
3713 * NtUserSetInternalWindowPos (win32u.@)
3715 void WINAPI NtUserSetInternalWindowPos( HWND hwnd, UINT cmd, RECT *rect, POINT *pt )
3717 WINDOWPLACEMENT wndpl;
3718 UINT flags;
3720 wndpl.length = sizeof(wndpl);
3721 wndpl.showCmd = cmd;
3722 wndpl.flags = flags = 0;
3724 if (pt)
3726 flags |= PLACE_MIN;
3727 wndpl.flags |= WPF_SETMINPOSITION;
3728 wndpl.ptMinPosition = *pt;
3730 if( rect )
3732 flags |= PLACE_RECT;
3733 wndpl.rcNormalPosition = *rect;
3735 set_window_placement( hwnd, &wndpl, flags );
3738 /***********************************************************************
3739 * win_set_flags
3741 * Set the flags of a window and return the previous value.
3743 UINT win_set_flags( HWND hwnd, UINT set_mask, UINT clear_mask )
3745 WND *win = get_win_ptr( hwnd );
3746 UINT ret;
3748 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return 0;
3749 ret = win->flags;
3750 win->flags = (ret & ~clear_mask) | set_mask;
3751 release_win_ptr( win );
3752 return ret;
3755 /*******************************************************************
3756 * can_activate_window
3758 * Check if we can activate the specified window.
3760 static BOOL can_activate_window( HWND hwnd )
3762 LONG style;
3764 if (!hwnd) return FALSE;
3765 style = get_window_long( hwnd, GWL_STYLE );
3766 if (!(style & WS_VISIBLE)) return FALSE;
3767 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
3768 return !(style & WS_DISABLED);
3771 /*******************************************************************
3772 * activate_other_window
3774 * Activates window other than hwnd.
3776 static void activate_other_window( HWND hwnd )
3778 HWND hwnd_to, fg;
3780 if ((get_window_long( hwnd, GWL_STYLE ) & WS_POPUP) &&
3781 (hwnd_to = get_window_relative( hwnd, GW_OWNER )))
3783 hwnd_to = NtUserGetAncestor( hwnd_to, GA_ROOT );
3784 if (can_activate_window( hwnd_to )) goto done;
3787 hwnd_to = hwnd;
3788 for (;;)
3790 if (!(hwnd_to = get_window_relative( hwnd_to, GW_HWNDNEXT ))) break;
3791 if (can_activate_window( hwnd_to )) goto done;
3794 hwnd_to = get_window_relative( get_desktop_window(), GW_CHILD );
3795 for (;;)
3797 if (hwnd_to == hwnd)
3799 hwnd_to = 0;
3800 break;
3802 if (can_activate_window( hwnd_to )) goto done;
3803 if (!(hwnd_to = get_window_relative( hwnd_to, GW_HWNDNEXT ))) break;
3806 done:
3807 fg = NtUserGetForegroundWindow();
3808 TRACE( "win = %p fg = %p\n", hwnd_to, fg );
3809 if (!fg || hwnd == fg)
3811 if (set_foreground_window( hwnd_to, FALSE )) return;
3813 if (NtUserSetActiveWindow( hwnd_to )) NtUserSetActiveWindow( 0 );
3816 /*******************************************************************
3817 * send_parent_notify
3819 static void send_parent_notify( HWND hwnd, UINT msg )
3821 if ((get_window_long( hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD &&
3822 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY))
3824 HWND parent = get_parent( hwnd );
3825 if (parent && parent != get_desktop_window())
3826 send_message( parent, WM_PARENTNOTIFY,
3827 MAKEWPARAM( msg, get_window_long( hwnd, GWLP_ID )), (LPARAM)hwnd );
3831 /*******************************************************************
3832 * get_min_max_info
3834 * Get the minimized and maximized information for a window.
3836 MINMAXINFO get_min_max_info( HWND hwnd )
3838 LONG style = get_window_long( hwnd, GWL_STYLE );
3839 LONG exstyle = get_window_long( hwnd, GWL_EXSTYLE );
3840 DPI_AWARENESS_CONTEXT context;
3841 RECT rc_work, rc_primary;
3842 LONG adjusted_style;
3843 MINMAXINFO minmax;
3844 INT xinc, yinc;
3845 RECT rc;
3846 WND *win;
3848 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
3850 /* Compute default values */
3852 get_window_rect( hwnd, &rc, get_thread_dpi() );
3853 minmax.ptReserved.x = rc.left;
3854 minmax.ptReserved.y = rc.top;
3856 if ((style & WS_CAPTION) == WS_CAPTION)
3857 adjusted_style = style & ~WS_BORDER; /* WS_CAPTION = WS_DLGFRAME | WS_BORDER */
3858 else
3859 adjusted_style = style;
3861 get_client_rect( NtUserGetAncestor( hwnd, GA_PARENT ), &rc );
3862 AdjustWindowRectEx( &rc, adjusted_style, (style & WS_POPUP) && get_menu( hwnd ), exstyle );
3864 xinc = -rc.left;
3865 yinc = -rc.top;
3867 minmax.ptMaxSize.x = rc.right - rc.left;
3868 minmax.ptMaxSize.y = rc.bottom - rc.top;
3869 if (style & (WS_DLGFRAME | WS_BORDER))
3871 minmax.ptMinTrackSize.x = get_system_metrics( SM_CXMINTRACK );
3872 minmax.ptMinTrackSize.y = get_system_metrics( SM_CYMINTRACK );
3874 else
3876 minmax.ptMinTrackSize.x = 2 * xinc;
3877 minmax.ptMinTrackSize.y = 2 * yinc;
3879 minmax.ptMaxTrackSize.x = get_system_metrics( SM_CXMAXTRACK );
3880 minmax.ptMaxTrackSize.y = get_system_metrics( SM_CYMAXTRACK );
3881 minmax.ptMaxPosition.x = -xinc;
3882 minmax.ptMaxPosition.y = -yinc;
3884 if ((win = get_win_ptr( hwnd )) && win != WND_DESKTOP && win != WND_OTHER_PROCESS)
3886 if (!empty_point( win->max_pos )) minmax.ptMaxPosition = win->max_pos;
3887 release_win_ptr( win );
3890 send_message( hwnd, WM_GETMINMAXINFO, 0, (LPARAM)&minmax );
3892 /* if the app didn't change the values, adapt them for the current monitor */
3894 if (get_work_rect( hwnd, &rc_work ))
3896 rc_primary = get_primary_monitor_rect( get_thread_dpi() );
3897 if (minmax.ptMaxSize.x == (rc_primary.right - rc_primary.left) + 2 * xinc &&
3898 minmax.ptMaxSize.y == (rc_primary.bottom - rc_primary.top) + 2 * yinc)
3900 minmax.ptMaxSize.x = (rc_work.right - rc_work.left) + 2 * xinc;
3901 minmax.ptMaxSize.y = (rc_work.bottom - rc_work.top) + 2 * yinc;
3903 if (minmax.ptMaxPosition.x == -xinc && minmax.ptMaxPosition.y == -yinc)
3905 minmax.ptMaxPosition.x = rc_work.left - xinc;
3906 minmax.ptMaxPosition.y = rc_work.top - yinc;
3910 TRACE( "%d %d / %d %d / %d %d / %d %d\n",
3911 (int)minmax.ptMaxSize.x, (int)minmax.ptMaxSize.y,
3912 (int)minmax.ptMaxPosition.x, (int)minmax.ptMaxPosition.y,
3913 (int)minmax.ptMaxTrackSize.x, (int)minmax.ptMaxTrackSize.y,
3914 (int)minmax.ptMinTrackSize.x, (int)minmax.ptMinTrackSize.y );
3916 minmax.ptMaxTrackSize.x = max( minmax.ptMaxTrackSize.x, minmax.ptMinTrackSize.x );
3917 minmax.ptMaxTrackSize.y = max( minmax.ptMaxTrackSize.y, minmax.ptMinTrackSize.y );
3919 SetThreadDpiAwarenessContext( context );
3920 return minmax;
3923 static POINT get_first_minimized_child_pos( const RECT *parent, const MINIMIZEDMETRICS *mm,
3924 int width, int height )
3926 POINT ret;
3928 if (mm->iArrange & ARW_STARTRIGHT)
3929 ret.x = parent->right - mm->iHorzGap - width;
3930 else
3931 ret.x = parent->left + mm->iHorzGap;
3932 if (mm->iArrange & ARW_STARTTOP)
3933 ret.y = parent->top + mm->iVertGap;
3934 else
3935 ret.y = parent->bottom - mm->iVertGap - height;
3937 return ret;
3940 static void get_next_minimized_child_pos( const RECT *parent, const MINIMIZEDMETRICS *mm,
3941 int width, int height, POINT *pos )
3943 BOOL next;
3945 if (mm->iArrange & ARW_UP) /* == ARW_DOWN */
3947 if (mm->iArrange & ARW_STARTTOP)
3949 pos->y += height + mm->iVertGap;
3950 if ((next = pos->y + height > parent->bottom))
3951 pos->y = parent->top + mm->iVertGap;
3953 else
3955 pos->y -= height + mm->iVertGap;
3956 if ((next = pos->y < parent->top))
3957 pos->y = parent->bottom - mm->iVertGap - height;
3960 if (next)
3962 if (mm->iArrange & ARW_STARTRIGHT)
3963 pos->x -= width + mm->iHorzGap;
3964 else
3965 pos->x += width + mm->iHorzGap;
3968 else
3970 if (mm->iArrange & ARW_STARTRIGHT)
3972 pos->x -= width + mm->iHorzGap;
3973 if ((next = pos->x < parent->left))
3974 pos->x = parent->right - mm->iHorzGap - width;
3976 else
3978 pos->x += width + mm->iHorzGap;
3979 if ((next = pos->x + width > parent->right))
3980 pos->x = parent->left + mm->iHorzGap;
3983 if (next)
3985 if (mm->iArrange & ARW_STARTTOP)
3986 pos->y += height + mm->iVertGap;
3987 else
3988 pos->y -= height + mm->iVertGap;
3993 static POINT get_minimized_pos( HWND hwnd, POINT pt )
3995 RECT rect, parent_rect;
3996 HWND parent, child;
3997 HRGN hrgn, tmp;
3998 MINIMIZEDMETRICS metrics;
3999 int width, height;
4001 parent = NtUserGetAncestor( hwnd, GA_PARENT );
4002 if (parent == get_desktop_window())
4004 MONITORINFO mon_info;
4005 HMONITOR monitor = monitor_from_window( hwnd, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
4007 mon_info.cbSize = sizeof( mon_info );
4008 get_monitor_info( monitor, &mon_info );
4009 parent_rect = mon_info.rcWork;
4011 else get_client_rect( parent, &parent_rect );
4013 if (pt.x >= parent_rect.left && (pt.x + get_system_metrics( SM_CXMINIMIZED ) < parent_rect.right) &&
4014 pt.y >= parent_rect.top && (pt.y + get_system_metrics( SM_CYMINIMIZED ) < parent_rect.bottom))
4015 return pt; /* The icon already has a suitable position */
4017 width = get_system_metrics( SM_CXMINIMIZED );
4018 height = get_system_metrics( SM_CYMINIMIZED );
4020 metrics.cbSize = sizeof(metrics);
4021 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(metrics), &metrics, 0 );
4023 /* Check if another icon already occupies this spot */
4024 /* FIXME: this is completely inefficient */
4026 hrgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
4027 tmp = NtGdiCreateRectRgn( 0, 0, 0, 0 );
4028 for (child = get_window_relative( parent, GW_CHILD );
4029 child;
4030 child = get_window_relative( child, GW_HWNDNEXT ))
4032 if (child == hwnd) continue;
4033 if ((get_window_long( child, GWL_STYLE ) & (WS_VISIBLE|WS_MINIMIZE)) != (WS_VISIBLE|WS_MINIMIZE))
4034 continue;
4035 if (get_window_rects( child, COORDS_PARENT, &rect, NULL, get_thread_dpi() ))
4037 NtGdiSetRectRgn( tmp, rect.left, rect.top, rect.right, rect.bottom );
4038 NtGdiCombineRgn( hrgn, hrgn, tmp, RGN_OR );
4041 NtGdiDeleteObjectApp( tmp );
4043 pt = get_first_minimized_child_pos( &parent_rect, &metrics, width, height );
4044 for (;;)
4046 SetRect( &rect, pt.x, pt.y, pt.x + width, pt.y + height );
4047 if (!NtGdiRectInRegion( hrgn, &rect ))
4048 break;
4050 get_next_minimized_child_pos( &parent_rect, &metrics, width, height, &pt );
4053 NtGdiDeleteObjectApp( hrgn );
4054 return pt;
4057 /***********************************************************************
4058 * window_min_maximize
4060 static UINT window_min_maximize( HWND hwnd, UINT cmd, RECT *rect )
4062 UINT swp_flags = 0;
4063 LONG old_style;
4064 MINMAXINFO minmax;
4065 WINDOWPLACEMENT wpl;
4067 TRACE( "%p %u\n", hwnd, cmd );
4069 wpl.length = sizeof(wpl);
4070 NtUserGetWindowPlacement( hwnd, &wpl );
4072 if (call_hooks( WH_CBT, HCBT_MINMAX, (WPARAM)hwnd, cmd, 0 ))
4073 return SWP_NOSIZE | SWP_NOMOVE;
4075 if (is_iconic( hwnd ))
4077 switch (cmd)
4079 case SW_SHOWMINNOACTIVE:
4080 case SW_SHOWMINIMIZED:
4081 case SW_FORCEMINIMIZE:
4082 case SW_MINIMIZE:
4083 wpl.ptMinPosition = get_minimized_pos( hwnd, wpl.ptMinPosition );
4085 SetRect( rect, wpl.ptMinPosition.x, wpl.ptMinPosition.y,
4086 wpl.ptMinPosition.x + get_system_metrics( SM_CXMINIMIZED ),
4087 wpl.ptMinPosition.y + get_system_metrics( SM_CYMINIMIZED ));
4088 return SWP_NOSIZE | SWP_NOMOVE;
4090 if (!send_message( hwnd, WM_QUERYOPEN, 0, 0 )) return SWP_NOSIZE | SWP_NOMOVE;
4091 swp_flags |= SWP_NOCOPYBITS;
4094 switch( cmd )
4096 case SW_SHOWMINNOACTIVE:
4097 case SW_SHOWMINIMIZED:
4098 case SW_FORCEMINIMIZE:
4099 case SW_MINIMIZE:
4100 if (is_zoomed( hwnd )) win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
4101 else win_set_flags( hwnd, 0, WIN_RESTORE_MAX );
4103 if (get_focus() == hwnd)
4105 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD)
4106 NtUserSetFocus( NtUserGetAncestor( hwnd, GA_PARENT ));
4107 else
4108 NtUserSetFocus( 0 );
4111 old_style = set_window_style( hwnd, WS_MINIMIZE, WS_MAXIMIZE );
4113 wpl.ptMinPosition = get_minimized_pos( hwnd, wpl.ptMinPosition );
4115 if (!(old_style & WS_MINIMIZE)) swp_flags |= SWP_STATECHANGED;
4116 SetRect( rect, wpl.ptMinPosition.x, wpl.ptMinPosition.y,
4117 wpl.ptMinPosition.x + get_system_metrics(SM_CXMINIMIZED),
4118 wpl.ptMinPosition.y + get_system_metrics(SM_CYMINIMIZED) );
4119 swp_flags |= SWP_NOCOPYBITS;
4120 break;
4122 case SW_MAXIMIZE:
4123 old_style = get_window_long( hwnd, GWL_STYLE );
4124 if ((old_style & WS_MAXIMIZE) && (old_style & WS_VISIBLE)) return SWP_NOSIZE | SWP_NOMOVE;
4126 minmax = get_min_max_info( hwnd );
4128 old_style = set_window_style( hwnd, WS_MAXIMIZE, WS_MINIMIZE );
4129 if (old_style & WS_MINIMIZE)
4130 win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
4132 if (!(old_style & WS_MAXIMIZE)) swp_flags |= SWP_STATECHANGED;
4133 SetRect( rect, minmax.ptMaxPosition.x, minmax.ptMaxPosition.y,
4134 minmax.ptMaxPosition.x + minmax.ptMaxSize.x,
4135 minmax.ptMaxPosition.y + minmax.ptMaxSize.y );
4136 break;
4138 case SW_SHOWNOACTIVATE:
4139 win_set_flags( hwnd, 0, WIN_RESTORE_MAX );
4140 /* fall through */
4141 case SW_SHOWNORMAL:
4142 case SW_RESTORE:
4143 case SW_SHOWDEFAULT: /* FIXME: should have its own handler */
4144 old_style = set_window_style( hwnd, 0, WS_MINIMIZE | WS_MAXIMIZE );
4145 if (old_style & WS_MINIMIZE)
4147 if (win_get_flags( hwnd ) & WIN_RESTORE_MAX)
4149 /* Restore to maximized position */
4150 minmax = get_min_max_info( hwnd );
4151 set_window_style( hwnd, WS_MAXIMIZE, 0 );
4152 swp_flags |= SWP_STATECHANGED;
4153 SetRect( rect, minmax.ptMaxPosition.x, minmax.ptMaxPosition.y,
4154 minmax.ptMaxPosition.x + minmax.ptMaxSize.x,
4155 minmax.ptMaxPosition.y + minmax.ptMaxSize.y );
4156 break;
4159 else if (!(old_style & WS_MAXIMIZE)) break;
4161 swp_flags |= SWP_STATECHANGED;
4163 /* Restore to normal position */
4165 *rect = wpl.rcNormalPosition;
4166 break;
4169 return swp_flags;
4172 /* see ArrangeIconicWindows */
4173 static UINT arrange_iconic_windows( HWND parent )
4175 int width, height, count = 0;
4176 MINIMIZEDMETRICS metrics;
4177 RECT parent_rect;
4178 HWND child;
4179 POINT pt;
4181 metrics.cbSize = sizeof(metrics);
4182 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(metrics), &metrics, 0 );
4183 width = get_system_metrics( SM_CXMINIMIZED );
4184 height = get_system_metrics( SM_CYMINIMIZED );
4186 if (parent == get_desktop_window())
4188 MONITORINFO mon_info;
4189 HMONITOR monitor = monitor_from_window( 0, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
4191 mon_info.cbSize = sizeof( mon_info );
4192 get_monitor_info( monitor, &mon_info );
4193 parent_rect = mon_info.rcWork;
4195 else get_client_rect( parent, &parent_rect );
4197 pt = get_first_minimized_child_pos( &parent_rect, &metrics, width, height );
4199 child = get_window_relative( parent, GW_CHILD );
4200 while (child)
4202 if (is_iconic( child ))
4204 NtUserSetWindowPos( child, 0, pt.x, pt.y, 0, 0,
4205 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
4206 get_next_minimized_child_pos( &parent_rect, &metrics, width, height, &pt );
4207 count++;
4209 child = get_window_relative( child, GW_HWNDNEXT );
4211 return count;
4214 /*******************************************************************
4215 * update_window_state
4217 * Trigger an update of the window's driver state and surface.
4219 void update_window_state( HWND hwnd )
4221 DPI_AWARENESS_CONTEXT context;
4222 RECT window_rect, client_rect, valid_rects[2];
4224 if (!is_current_thread_window( hwnd ))
4226 NtUserPostMessage( hwnd, WM_WINE_UPDATEWINDOWSTATE, 0, 0 );
4227 return;
4230 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
4231 get_window_rects( hwnd, COORDS_PARENT, &window_rect, &client_rect, get_thread_dpi() );
4232 valid_rects[0] = valid_rects[1] = client_rect;
4233 apply_window_pos( hwnd, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE |
4234 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW,
4235 &window_rect, &client_rect, valid_rects );
4236 SetThreadDpiAwarenessContext( context );
4239 /***********************************************************************
4240 * show_window
4242 * Implementation of ShowWindow and ShowWindowAsync.
4244 static BOOL show_window( HWND hwnd, INT cmd )
4246 WND *win;
4247 HWND parent;
4248 DPI_AWARENESS_CONTEXT context;
4249 LONG style = get_window_long( hwnd, GWL_STYLE );
4250 BOOL was_visible = (style & WS_VISIBLE) != 0;
4251 BOOL show_flag = TRUE;
4252 RECT newPos = {0, 0, 0, 0};
4253 UINT new_swp, swp = 0;
4255 TRACE( "hwnd=%p, cmd=%d, was_visible %d\n", hwnd, cmd, was_visible );
4257 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
4259 switch(cmd)
4261 case SW_HIDE:
4262 if (!was_visible) goto done;
4263 show_flag = FALSE;
4264 swp |= SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE;
4265 if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4266 break;
4268 case SW_SHOWMINNOACTIVE:
4269 case SW_MINIMIZE:
4270 case SW_FORCEMINIMIZE: /* FIXME: Does not work if thread is hung. */
4271 swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4272 /* fall through */
4273 case SW_SHOWMINIMIZED:
4274 swp |= SWP_SHOWWINDOW | SWP_FRAMECHANGED;
4275 swp |= window_min_maximize( hwnd, cmd, &newPos );
4276 if ((style & WS_MINIMIZE) && was_visible) goto done;
4277 break;
4279 case SW_SHOWMAXIMIZED: /* same as SW_MAXIMIZE */
4280 if (!was_visible) swp |= SWP_SHOWWINDOW;
4281 swp |= SWP_FRAMECHANGED;
4282 swp |= window_min_maximize( hwnd, SW_MAXIMIZE, &newPos );
4283 if ((style & WS_MAXIMIZE) && was_visible) goto done;
4284 break;
4286 case SW_SHOWNA:
4287 swp |= SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
4288 if (style & WS_CHILD) swp |= SWP_NOZORDER;
4289 break;
4291 case SW_SHOW:
4292 if (was_visible) goto done;
4293 swp |= SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
4294 if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4295 break;
4297 case SW_SHOWNOACTIVATE:
4298 swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4299 /* fall through */
4300 case SW_RESTORE:
4301 /* fall through */
4302 case SW_SHOWNORMAL: /* same as SW_NORMAL: */
4303 case SW_SHOWDEFAULT: /* FIXME: should have its own handler */
4304 if (!was_visible) swp |= SWP_SHOWWINDOW;
4305 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
4307 swp |= SWP_FRAMECHANGED;
4308 swp |= window_min_maximize( hwnd, cmd, &newPos );
4310 else
4312 if (was_visible) goto done;
4313 swp |= SWP_NOSIZE | SWP_NOMOVE;
4315 if (style & WS_CHILD && !(swp & SWP_STATECHANGED)) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4316 break;
4318 default:
4319 goto done;
4322 if ((show_flag != was_visible || cmd == SW_SHOWNA) && cmd != SW_SHOWMAXIMIZED && !(swp & SWP_STATECHANGED))
4324 send_message( hwnd, WM_SHOWWINDOW, show_flag, 0 );
4325 if (!is_window( hwnd )) goto done;
4328 if (IsRectEmpty( &newPos )) new_swp = swp;
4329 else if ((new_swp = user_driver->pShowWindow( hwnd, cmd, &newPos, swp )) == ~0)
4331 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) new_swp = swp;
4332 else if (is_iconic( hwnd ) && (newPos.left != -32000 || newPos.top != -32000))
4334 OffsetRect( &newPos, -32000 - newPos.left, -32000 - newPos.top );
4335 new_swp = swp & ~(SWP_NOMOVE | SWP_NOCLIENTMOVE);
4337 else new_swp = swp;
4339 swp = new_swp;
4341 parent = NtUserGetAncestor( hwnd, GA_PARENT );
4342 if (parent && !is_window_visible( parent ) && !(swp & SWP_STATECHANGED))
4344 /* if parent is not visible simply toggle WS_VISIBLE and return */
4345 if (show_flag) set_window_style( hwnd, WS_VISIBLE, 0 );
4346 else set_window_style( hwnd, 0, WS_VISIBLE );
4348 else
4349 NtUserSetWindowPos( hwnd, HWND_TOP, newPos.left, newPos.top,
4350 newPos.right - newPos.left, newPos.bottom - newPos.top, swp );
4352 if (cmd == SW_HIDE)
4354 HWND hFocus;
4356 /* FIXME: This will cause the window to be activated irrespective
4357 * of whether it is owned by the same thread. Has to be done
4358 * asynchronously.
4361 if (hwnd == get_active_window()) activate_other_window( hwnd );
4363 /* Revert focus to parent */
4364 hFocus = get_focus();
4365 if (hwnd == hFocus)
4367 HWND parent = NtUserGetAncestor(hwnd, GA_PARENT);
4368 if (parent == get_desktop_window()) parent = 0;
4369 NtUserSetFocus(parent);
4371 goto done;
4374 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) goto done;
4376 if (win->flags & WIN_NEED_SIZE)
4378 /* should happen only in CreateWindowEx() */
4379 int wParam = SIZE_RESTORED;
4380 RECT client;
4381 LPARAM lparam;
4383 get_window_rects( hwnd, COORDS_PARENT, NULL, &client, get_thread_dpi() );
4384 lparam = MAKELONG( client.right - client.left, client.bottom - client.top );
4385 win->flags &= ~WIN_NEED_SIZE;
4386 if (win->dwStyle & WS_MAXIMIZE) wParam = SIZE_MAXIMIZED;
4387 else if (win->dwStyle & WS_MINIMIZE)
4389 wParam = SIZE_MINIMIZED;
4390 lparam = 0;
4392 release_win_ptr( win );
4394 send_message( hwnd, WM_SIZE, wParam, lparam );
4395 send_message( hwnd, WM_MOVE, 0, MAKELONG( client.left, client.top ));
4397 else release_win_ptr( win );
4399 /* if previous state was minimized Windows sets focus to the window */
4400 if (style & WS_MINIMIZE)
4402 NtUserSetFocus( hwnd );
4403 /* Send a WM_ACTIVATE message for a top level window, even if the window is already active */
4404 if (NtUserGetAncestor( hwnd, GA_ROOT ) == hwnd && !(swp & SWP_NOACTIVATE))
4405 send_message( hwnd, WM_ACTIVATE, WA_ACTIVE, 0 );
4408 done:
4409 SetThreadDpiAwarenessContext( context );
4410 return was_visible;
4413 /***********************************************************************
4414 * NtUserShowWindowAsync (win32u.@)
4416 * doesn't wait; returns immediately.
4417 * used by threads to toggle windows in other (possibly hanging) threads
4419 BOOL WINAPI NtUserShowWindowAsync( HWND hwnd, INT cmd )
4421 HWND full_handle;
4423 if (is_broadcast(hwnd))
4425 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4426 return FALSE;
4429 if ((full_handle = is_current_thread_window( hwnd )))
4430 return show_window( full_handle, cmd );
4432 return NtUserMessageCall( hwnd, WM_WINE_SHOWWINDOW, cmd, 0, 0,
4433 NtUserSendNotifyMessage, FALSE );
4436 /***********************************************************************
4437 * NtUserShowWindow (win32u.@)
4439 BOOL WINAPI NtUserShowWindow( HWND hwnd, INT cmd )
4441 HWND full_handle;
4443 if (is_broadcast(hwnd))
4445 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4446 return FALSE;
4448 if ((full_handle = is_current_thread_window( hwnd )))
4449 return show_window( full_handle, cmd );
4451 if ((cmd == SW_HIDE) && !(get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE))
4452 return FALSE;
4454 if ((cmd == SW_SHOW) && (get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE))
4455 return TRUE;
4457 return send_message( hwnd, WM_WINE_SHOWWINDOW, cmd, 0 );
4460 /* see ShowOwnedPopups */
4461 BOOL show_owned_popups( HWND owner, BOOL show )
4463 int count = 0;
4464 HWND *win_array = list_window_children( 0, get_desktop_window(), NULL, 0 );
4466 if (!win_array) return TRUE;
4468 while (win_array[count]) count++;
4469 while (--count >= 0)
4471 if (get_window_relative( win_array[count], GW_OWNER ) != owner) continue;
4472 if (show)
4474 if (win_get_flags( win_array[count] ) & WIN_NEEDS_SHOW_OWNEDPOPUP)
4475 /* In Windows, ShowOwnedPopups(TRUE) generates
4476 * WM_SHOWWINDOW messages with SW_PARENTOPENING,
4477 * regardless of the state of the owner
4479 send_message( win_array[count], WM_SHOWWINDOW, SW_SHOWNORMAL, SW_PARENTOPENING );
4481 else
4483 if (get_window_long( win_array[count], GWL_STYLE ) & WS_VISIBLE)
4484 /* In Windows, ShowOwnedPopups(FALSE) generates
4485 * WM_SHOWWINDOW messages with SW_PARENTCLOSING,
4486 * regardless of the state of the owner
4488 send_message( win_array[count], WM_SHOWWINDOW, SW_HIDE, SW_PARENTCLOSING );
4492 free( win_array );
4493 return TRUE;
4496 /*******************************************************************
4497 * NtUserFlashWindowEx (win32u.@)
4499 BOOL WINAPI NtUserFlashWindowEx( FLASHWINFO *info )
4501 WND *win;
4503 TRACE( "%p\n", info );
4505 if (!info)
4507 RtlSetLastWin32Error( ERROR_NOACCESS );
4508 return FALSE;
4511 if (!info->hwnd || info->cbSize != sizeof(FLASHWINFO) || !is_window( info->hwnd ))
4513 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4514 return FALSE;
4516 FIXME( "%p - semi-stub\n", info );
4518 if (is_iconic( info->hwnd ))
4520 NtUserRedrawWindow( info->hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_FRAME );
4522 win = get_win_ptr( info->hwnd );
4523 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
4524 if (info->dwFlags & FLASHW_CAPTION && !(win->flags & WIN_NCACTIVATED))
4526 win->flags |= WIN_NCACTIVATED;
4528 else if (!info->dwFlags)
4530 win->flags &= ~WIN_NCACTIVATED;
4532 release_win_ptr( win );
4533 user_driver->pFlashWindowEx( info );
4534 return TRUE;
4536 else
4538 WPARAM wparam;
4539 HWND hwnd = info->hwnd;
4541 win = get_win_ptr( hwnd );
4542 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
4543 hwnd = win->obj.handle; /* make it a full handle */
4545 if (info->dwFlags) wparam = !(win->flags & WIN_NCACTIVATED);
4546 else wparam = (hwnd == NtUserGetForegroundWindow());
4548 release_win_ptr( win );
4550 if (!info->dwFlags || info->dwFlags & FLASHW_CAPTION)
4551 send_message( hwnd, WM_NCACTIVATE, wparam, 0 );
4553 user_driver->pFlashWindowEx( info );
4554 return wparam;
4558 /* see GetWindowContextHelpId */
4559 DWORD get_window_context_help_id( HWND hwnd )
4561 DWORD retval;
4562 WND *win = get_win_ptr( hwnd );
4563 if (!win || win == WND_DESKTOP) return 0;
4564 if (win == WND_OTHER_PROCESS)
4566 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4567 return 0;
4569 retval = win->helpContext;
4570 release_win_ptr( win );
4571 return retval;
4574 /* see SetWindowContextHelpId */
4575 static BOOL set_window_context_help_id( HWND hwnd, DWORD id )
4577 WND *win = get_win_ptr( hwnd );
4578 if (!win || win == WND_DESKTOP) return FALSE;
4579 if (win == WND_OTHER_PROCESS)
4581 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4582 return FALSE;
4584 win->helpContext = id;
4585 release_win_ptr( win );
4586 return TRUE;
4589 /***********************************************************************
4590 * NtUserInternalGetWindowIcon (win32u.@)
4592 HICON WINAPI NtUserInternalGetWindowIcon( HWND hwnd, UINT type )
4594 WND *win = get_win_ptr( hwnd );
4595 HICON ret;
4597 TRACE( "hwnd %p, type %#x\n", hwnd, type );
4599 if (!win)
4601 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
4602 return 0;
4604 if (win == WND_OTHER_PROCESS || win == WND_DESKTOP)
4606 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4607 return 0;
4610 switch (type)
4612 case ICON_BIG:
4613 ret = win->hIcon;
4614 if (!ret) ret = (HICON)get_class_long_ptr( hwnd, GCLP_HICON, FALSE );
4615 break;
4617 case ICON_SMALL:
4618 case ICON_SMALL2:
4619 ret = win->hIconSmall ? win->hIconSmall : win->hIconSmall2;
4620 if (!ret) ret = (HICON)get_class_long_ptr( hwnd, GCLP_HICONSM, FALSE );
4621 if (!ret) ret = (HICON)get_class_long_ptr( hwnd, GCLP_HICON, FALSE );
4622 break;
4624 default:
4625 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4626 release_win_ptr( win );
4627 return 0;
4629 release_win_ptr( win );
4631 if (!ret) ret = LoadImageW( 0, (const WCHAR *)IDI_APPLICATION, IMAGE_ICON,
4632 0, 0, LR_SHARED | LR_DEFAULTSIZE );
4634 return CopyImage( ret, IMAGE_ICON, 0, 0, 0 );
4637 /***********************************************************************
4638 * send_destroy_message
4640 static void send_destroy_message( HWND hwnd )
4642 GUITHREADINFO info;
4644 info.cbSize = sizeof(info);
4645 if (NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ))
4647 if (hwnd == info.hwndCaret) destroy_caret();
4648 if (hwnd == info.hwndActive) activate_other_window( hwnd );
4651 if (hwnd == NtUserGetClipboardOwner()) release_clipboard_owner( hwnd );
4653 send_message( hwnd, WM_DESTROY, 0, 0);
4656 * This WM_DESTROY message can trigger re-entrant calls to DestroyWindow
4657 * make sure that the window still exists when we come back.
4659 if (is_window(hwnd))
4661 HWND *children;
4662 int i;
4664 if (!(children = list_window_children( 0, hwnd, NULL, 0 ))) return;
4666 for (i = 0; children[i]; i++)
4668 if (is_window( children[i] )) send_destroy_message( children[i] );
4670 free( children );
4672 else
4673 WARN( "\tdestroyed itself while in WM_DESTROY!\n" );
4676 /***********************************************************************
4677 * free_window_handle
4679 * Free a window handle.
4681 static void free_window_handle( HWND hwnd )
4683 WND *win;
4685 TRACE( "\n" );
4687 if ((win = get_user_handle_ptr( hwnd, NTUSER_OBJ_WINDOW )) && win != OBJ_OTHER_PROCESS)
4689 SERVER_START_REQ( destroy_window )
4691 req->handle = wine_server_user_handle( hwnd );
4692 wine_server_call( req );
4693 set_user_handle_ptr( hwnd, NULL );
4695 SERVER_END_REQ;
4696 user_unlock();
4697 free( win->pScroll );
4698 free( win->text );
4699 free( win );
4703 /***********************************************************************
4704 * destroy_window
4706 LRESULT destroy_window( HWND hwnd )
4708 struct window_surface *surface;
4709 HMENU menu = 0, sys_menu;
4710 WND *win;
4711 HWND *children;
4713 TRACE( "%p\n", hwnd );
4715 unregister_imm_window( hwnd );
4717 /* free child windows */
4718 if ((children = list_window_children( 0, hwnd, NULL, 0 )))
4720 int i;
4721 for (i = 0; children[i]; i++)
4723 if (is_current_thread_window( children[i] ))
4724 destroy_window( children[i] );
4725 else
4726 NtUserMessageCall( children[i], WM_WINE_DESTROYWINDOW, 0, 0,
4727 0, NtUserSendNotifyMessage, FALSE );
4729 free( children );
4732 /* Unlink now so we won't bother with the children later on */
4733 SERVER_START_REQ( set_parent )
4735 req->handle = wine_server_user_handle( hwnd );
4736 req->parent = 0;
4737 wine_server_call( req );
4739 SERVER_END_REQ;
4741 send_message( hwnd, WM_NCDESTROY, 0, 0 );
4743 /* FIXME: do we need to fake QS_MOUSEMOVE wakebit? */
4745 /* free resources associated with the window */
4747 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
4748 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
4749 menu = (HMENU)win->wIDmenu;
4750 sys_menu = win->hSysMenu;
4751 free_dce( win->dce, hwnd );
4752 win->dce = NULL;
4753 NtUserDestroyCursor( win->hIconSmall2, 0 );
4754 surface = win->surface;
4755 win->surface = NULL;
4756 release_win_ptr( win );
4758 NtUserDestroyMenu( menu );
4759 NtUserDestroyMenu( sys_menu );
4760 if (surface)
4762 register_window_surface( surface, NULL );
4763 window_surface_release( surface );
4766 user_driver->pDestroyWindow( hwnd );
4768 free_window_handle( hwnd );
4769 return 0;
4772 /***********************************************************************
4773 * NtUserDestroyWindow (win32u.@)
4775 BOOL WINAPI NtUserDestroyWindow( HWND hwnd )
4777 BOOL is_child;
4779 if (!(hwnd = is_current_thread_window( hwnd )) || is_desktop_window( hwnd ))
4781 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
4782 return FALSE;
4785 TRACE( "(%p)\n", hwnd );
4787 if (call_hooks( WH_CBT, HCBT_DESTROYWND, (WPARAM)hwnd, 0, 0 )) return FALSE;
4789 if (is_menu_active() == hwnd) NtUserEndMenu();
4791 is_child = (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) != 0;
4793 if (is_child)
4795 if (!is_exiting_thread( GetCurrentThreadId() ))
4796 send_parent_notify( hwnd, WM_DESTROY );
4798 else if (!get_window_relative( hwnd, GW_OWNER ))
4800 call_hooks( WH_SHELL, HSHELL_WINDOWDESTROYED, (WPARAM)hwnd, 0, 0 );
4801 /* FIXME: clean up palette - see "Internals" p.352 */
4804 if (!is_window( hwnd )) return TRUE;
4806 /* Hide the window */
4807 if (get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE)
4809 /* Only child windows receive WM_SHOWWINDOW in DestroyWindow() */
4810 if (is_child)
4811 NtUserShowWindow( hwnd, SW_HIDE );
4812 else
4813 NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE |
4814 SWP_NOZORDER | SWP_NOACTIVATE | SWP_HIDEWINDOW );
4817 if (!is_window( hwnd )) return TRUE;
4819 /* Recursively destroy child windows */
4820 if (!is_child)
4822 for (;;)
4824 BOOL got_one = FALSE;
4825 HWND *children;
4826 unsigned int i;
4828 if (!(children = list_window_children( 0, get_desktop_window(), NULL, 0 ))) break;
4830 for (i = 0; children[i]; i++)
4832 if (get_window_relative( children[i], GW_OWNER ) != hwnd) continue;
4833 if (is_current_thread_window( children[i] ))
4835 NtUserDestroyWindow( children[i] );
4836 got_one = TRUE;
4837 continue;
4839 set_window_owner( children[i], 0 );
4841 free( children );
4842 if (!got_one) break;
4846 send_destroy_message( hwnd );
4847 if (!is_window( hwnd )) return TRUE;
4849 destroy_window( hwnd );
4850 return TRUE;
4853 /*****************************************************************************
4854 * destroy_thread_windows
4856 * Destroy all window owned by the current thread.
4858 void destroy_thread_windows(void)
4860 WND *win, *free_list = NULL;
4861 HANDLE handle = 0;
4863 user_lock();
4864 while ((win = next_process_user_handle_ptr( &handle, NTUSER_OBJ_WINDOW )))
4866 if (win->tid != GetCurrentThreadId()) continue;
4867 free_dce( win->dce, win->obj.handle );
4868 set_user_handle_ptr( handle, NULL );
4869 win->obj.handle = free_list;
4870 free_list = win;
4872 if (free_list)
4874 SERVER_START_REQ( destroy_window )
4876 req->handle = 0; /* destroy all thread windows */
4877 wine_server_call( req );
4879 SERVER_END_REQ;
4881 user_unlock();
4883 while ((win = free_list))
4885 free_list = win->obj.handle;
4886 TRACE( "destroying %p\n", win );
4888 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD && win->wIDmenu)
4889 NtUserDestroyMenu( UlongToHandle(win->wIDmenu) );
4890 if (win->hSysMenu) NtUserDestroyMenu( win->hSysMenu );
4891 if (win->surface)
4893 register_window_surface( win->surface, NULL );
4894 window_surface_release( win->surface );
4896 free( win->pScroll );
4897 free( win->text );
4898 free( win );
4902 /***********************************************************************
4903 * create_window_handle
4905 * Create a window handle with the server.
4907 static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name,
4908 HINSTANCE instance, BOOL ansi,
4909 DWORD style, DWORD ex_style )
4911 DPI_AWARENESS awareness = get_thread_dpi_awareness();
4912 HWND handle = 0, full_parent = 0, full_owner = 0;
4913 struct tagCLASS *class = NULL;
4914 int extra_bytes = 0;
4915 UINT dpi = 0;
4916 WND *win;
4918 SERVER_START_REQ( create_window )
4920 req->parent = wine_server_user_handle( parent );
4921 req->owner = wine_server_user_handle( owner );
4922 req->instance = wine_server_client_ptr( instance );
4923 req->dpi = get_system_dpi();
4924 req->awareness = awareness;
4925 req->style = style;
4926 req->ex_style = ex_style;
4927 if (!(req->atom = get_int_atom_value( name )) && name->Length)
4928 wine_server_add_data( req, name->Buffer, name->Length );
4929 if (!wine_server_call_err( req ))
4931 handle = wine_server_ptr_handle( reply->handle );
4932 full_parent = wine_server_ptr_handle( reply->parent );
4933 full_owner = wine_server_ptr_handle( reply->owner );
4934 extra_bytes = reply->extra;
4935 dpi = reply->dpi;
4936 awareness = reply->awareness;
4937 class = wine_server_get_ptr( reply->class_ptr );
4940 SERVER_END_REQ;
4942 if (!handle)
4944 WARN( "error %d creating window\n", (int)RtlGetLastWin32Error() );
4945 return NULL;
4948 if (!(win = calloc( 1, FIELD_OFFSET(WND, wExtra) + extra_bytes )))
4950 SERVER_START_REQ( destroy_window )
4952 req->handle = wine_server_user_handle( handle );
4953 wine_server_call( req );
4955 SERVER_END_REQ;
4956 RtlSetLastWin32Error( ERROR_NOT_ENOUGH_MEMORY );
4957 return NULL;
4960 if (!parent) /* if parent is 0 we don't have a desktop window yet */
4962 struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
4964 if (name->Buffer == (const WCHAR *)DESKTOP_CLASS_ATOM)
4966 if (!thread_info->top_window) thread_info->top_window = HandleToUlong( full_parent ? full_parent : handle );
4967 else assert( full_parent == UlongToHandle( thread_info->top_window ));
4968 if (!thread_info->top_window) ERR_(win)( "failed to create desktop window\n" );
4969 else user_driver->pSetDesktopWindow( UlongToHandle( thread_info->top_window ));
4970 register_builtin_classes();
4972 else /* HWND_MESSAGE parent */
4974 if (!thread_info->msg_window && !full_parent)
4975 thread_info->msg_window = HandleToUlong( handle );
4979 user_lock();
4981 win->obj.handle = handle;
4982 win->obj.type = NTUSER_OBJ_WINDOW;
4983 win->parent = full_parent;
4984 win->owner = full_owner;
4985 win->class = class;
4986 win->winproc = get_class_winproc( class );
4987 win->cbWndExtra = extra_bytes;
4988 win->dpi = dpi;
4989 win->dpi_awareness = awareness;
4990 set_user_handle_ptr( handle, &win->obj );
4991 if (is_winproc_unicode( win->winproc, !ansi )) win->flags |= WIN_ISUNICODE;
4992 return win;
4995 static BOOL is_default_coord( int x )
4997 return x == CW_USEDEFAULT || x == 0x8000;
5000 /***********************************************************************
5001 * fix_cs_coordinates
5003 * Fix the coordinates and return default show mode in sw.
5005 static void fix_cs_coordinates( CREATESTRUCTW *cs, INT *sw )
5007 if (cs->style & (WS_CHILD | WS_POPUP))
5009 if (is_default_coord(cs->x)) cs->x = cs->y = 0;
5010 if (is_default_coord(cs->cx)) cs->cx = cs->cy = 0;
5012 else /* overlapped window */
5014 RTL_USER_PROCESS_PARAMETERS *params = NtCurrentTeb()->Peb->ProcessParameters;
5015 HMONITOR monitor;
5016 MONITORINFO mon_info;
5018 if (!is_default_coord( cs->x ) && !is_default_coord( cs->cx ) && !is_default_coord( cs->cy ))
5019 return;
5021 monitor = monitor_from_window( cs->hwndParent, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
5022 mon_info.cbSize = sizeof(mon_info);
5023 get_monitor_info( monitor, &mon_info );
5025 if (is_default_coord( cs->x ))
5027 if (!is_default_coord( cs->y )) *sw = cs->y;
5028 cs->x = (params->dwFlags & STARTF_USEPOSITION) ? params->dwX : mon_info.rcWork.left;
5029 cs->y = (params->dwFlags & STARTF_USEPOSITION) ? params->dwY : mon_info.rcWork.top;
5032 if (is_default_coord( cs->cx ))
5034 if (params->dwFlags & STARTF_USESIZE)
5036 cs->cx = params->dwXSize;
5037 cs->cy = params->dwYSize;
5039 else
5041 cs->cx = (mon_info.rcWork.right - mon_info.rcWork.left) * 3 / 4 - cs->x;
5042 cs->cy = (mon_info.rcWork.bottom - mon_info.rcWork.top) * 3 / 4 - cs->y;
5045 /* neither x nor cx are default. Check the y values.
5046 * In the trace we see Outlook and Outlook Express using
5047 * cy set to CW_USEDEFAULT when opening the address book.
5049 else if (is_default_coord( cs->cy ))
5051 FIXME( "Strange use of CW_USEDEFAULT in cy\n" );
5052 cs->cy = (mon_info.rcWork.bottom - mon_info.rcWork.top) * 3 / 4 - cs->y;
5057 /***********************************************************************
5058 * map_dpi_create_struct
5060 static void map_dpi_create_struct( CREATESTRUCTW *cs, UINT dpi_from, UINT dpi_to )
5062 if (!dpi_from && !dpi_to) return;
5063 if (!dpi_from || !dpi_to)
5065 POINT pt = { cs->x, cs->y };
5066 UINT mon_dpi = get_monitor_dpi( monitor_from_point( pt, MONITOR_DEFAULTTONEAREST, dpi_from ));
5067 if (!dpi_from) dpi_from = mon_dpi;
5068 else dpi_to = mon_dpi;
5070 if (dpi_from == dpi_to) return;
5071 cs->x = muldiv( cs->x, dpi_to, dpi_from );
5072 cs->y = muldiv( cs->y, dpi_to, dpi_from );
5073 cs->cx = muldiv( cs->cx, dpi_to, dpi_from );
5074 cs->cy = muldiv( cs->cy, dpi_to, dpi_from );
5077 /***********************************************************************
5078 * NtUserCreateWindowEx (win32u.@)
5080 HWND WINAPI NtUserCreateWindowEx( DWORD ex_style, UNICODE_STRING *class_name,
5081 UNICODE_STRING *version, UNICODE_STRING *window_name,
5082 DWORD style, INT x, INT y, INT cx, INT cy,
5083 HWND parent, HMENU menu, HINSTANCE instance, void *params,
5084 DWORD flags, HINSTANCE client_instance, DWORD unk, BOOL ansi )
5086 UINT win_dpi, thread_dpi = get_thread_dpi();
5087 DPI_AWARENESS_CONTEXT context;
5088 CBT_CREATEWNDW cbtc;
5089 HWND hwnd, owner = 0;
5090 CREATESTRUCTW cs;
5091 INT sw = SW_SHOW;
5092 RECT rect;
5093 WND *win;
5095 static const WCHAR messageW[] = {'M','e','s','s','a','g','e'};
5097 cs.lpCreateParams = params;
5098 cs.hInstance = client_instance ? client_instance : instance;
5099 cs.hMenu = menu;
5100 cs.hwndParent = parent;
5101 cs.style = style;
5102 cs.dwExStyle = ex_style;
5103 cs.lpszName = window_name ? window_name->Buffer : NULL;
5104 cs.lpszClass = class_name ? class_name->Buffer : NULL;
5105 cs.x = x;
5106 cs.y = y;
5107 cs.cx = cx;
5108 cs.cy = cy;
5110 /* Find the parent window */
5111 if (parent == HWND_MESSAGE)
5113 cs.hwndParent = parent = get_hwnd_message_parent();
5115 else if (parent)
5117 if ((cs.style & (WS_CHILD|WS_POPUP)) != WS_CHILD)
5119 owner = parent;
5120 parent = get_desktop_window();
5122 else
5124 DWORD parent_style = get_window_long( parent, GWL_EXSTYLE );
5125 if ((parent_style & WS_EX_LAYOUTRTL) && !(parent_style & WS_EX_NOINHERITLAYOUT))
5126 cs.dwExStyle |= WS_EX_LAYOUTRTL;
5129 else
5131 if ((cs.style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
5133 WARN( "No parent for child window\n" );
5134 RtlSetLastWin32Error( ERROR_TLW_WITH_WSCHILD );
5135 return 0; /* WS_CHILD needs a parent, but WS_POPUP doesn't */
5138 /* are we creating the desktop or HWND_MESSAGE parent itself? */
5139 if (class_name->Buffer != (LPCWSTR)DESKTOP_CLASS_ATOM &&
5140 (class_name->Length != sizeof(messageW) ||
5141 wcsnicmp( class_name->Buffer, messageW, ARRAYSIZE(messageW) )))
5143 if (get_process_layout() & LAYOUT_RTL) cs.dwExStyle |= WS_EX_LAYOUTRTL;
5144 parent = get_desktop_window();
5148 fix_cs_coordinates( &cs, &sw );
5149 cs.dwExStyle = fix_exstyle( cs.style, cs.dwExStyle );
5151 /* Create the window structure */
5153 style = cs.style & ~WS_VISIBLE;
5154 ex_style = cs.dwExStyle & ~WS_EX_LAYERED;
5155 if (!(win = create_window_handle( parent, owner, class_name, instance, ansi, style, ex_style )))
5156 return 0;
5157 hwnd = win->obj.handle;
5159 /* Fill the window structure */
5161 win->tid = GetCurrentThreadId();
5162 win->hInstance = cs.hInstance;
5163 win->text = NULL;
5164 win->dwStyle = style;
5165 win->dwExStyle = ex_style;
5166 win->wIDmenu = 0;
5167 win->helpContext = 0;
5168 win->pScroll = NULL;
5169 win->userdata = 0;
5170 win->hIcon = 0;
5171 win->hIconSmall = 0;
5172 win->hIconSmall2 = 0;
5173 win->hSysMenu = 0;
5175 win->min_pos.x = win->min_pos.y = -1;
5176 win->max_pos.x = win->max_pos.y = -1;
5177 SetRect( &win->normal_rect, cs.x, cs.y, cs.x + cs.cx, cs.y + cs.cy );
5179 if (win->dwStyle & WS_SYSMENU) NtUserSetSystemMenu( hwnd, 0 );
5181 win->imc = get_default_input_context();
5183 /* call the WH_CBT hook */
5185 release_win_ptr( win );
5186 cbtc.hwndInsertAfter = HWND_TOP;
5187 cbtc.lpcs = &cs;
5188 if (call_hooks( WH_CBT, HCBT_CREATEWND, (WPARAM)hwnd, (LPARAM)&cbtc, sizeof(cbtc) ))
5190 free_window_handle( hwnd );
5191 return 0;
5193 if (!(win = get_win_ptr( hwnd ))) return 0;
5196 * Correct the window styles.
5198 * It affects only the style loaded into the WND structure.
5201 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
5203 win->dwStyle |= WS_CLIPSIBLINGS;
5204 if (!(win->dwStyle & WS_POPUP)) win->dwStyle |= WS_CAPTION;
5207 win->dwExStyle = cs.dwExStyle;
5208 /* WS_EX_WINDOWEDGE depends on some other styles */
5209 if ((win->dwStyle & (WS_DLGFRAME | WS_THICKFRAME)) &&
5210 !(win->dwStyle & (WS_CHILD | WS_POPUP)))
5211 win->dwExStyle |= WS_EX_WINDOWEDGE;
5213 if (!(win->dwStyle & (WS_CHILD | WS_POPUP))) win->flags |= WIN_NEED_SIZE;
5215 SERVER_START_REQ( set_window_info )
5217 req->handle = wine_server_user_handle( hwnd );
5218 req->flags = SET_WIN_STYLE | SET_WIN_EXSTYLE | SET_WIN_INSTANCE | SET_WIN_UNICODE;
5219 req->style = win->dwStyle;
5220 req->ex_style = win->dwExStyle;
5221 req->instance = wine_server_client_ptr( win->hInstance );
5222 req->is_unicode = (win->flags & WIN_ISUNICODE) != 0;
5223 req->extra_offset = -1;
5224 wine_server_call( req );
5226 SERVER_END_REQ;
5228 /* Set the window menu */
5230 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
5232 if (cs.hMenu && !set_window_menu( hwnd, cs.hMenu ))
5234 release_win_ptr( win );
5235 free_window_handle( hwnd );
5236 return 0;
5239 else NtUserSetWindowLongPtr( hwnd, GWLP_ID, (ULONG_PTR)cs.hMenu, FALSE );
5241 win_dpi = win->dpi;
5242 release_win_ptr( win );
5244 if (parent) map_dpi_create_struct( &cs, thread_dpi, win_dpi );
5246 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
5248 /* send the WM_GETMINMAXINFO message and fix the size if needed */
5250 cx = cs.cx;
5251 cy = cs.cy;
5252 if ((cs.style & WS_THICKFRAME) || !(cs.style & (WS_POPUP | WS_CHILD)))
5254 MINMAXINFO info = get_min_max_info( hwnd );
5255 cx = max( min( cx, info.ptMaxTrackSize.x ), info.ptMinTrackSize.x );
5256 cy = max( min( cy, info.ptMaxTrackSize.y ), info.ptMinTrackSize.y );
5259 if (cx < 0) cx = 0;
5260 if (cy < 0) cy = 0;
5261 SetRect( &rect, cs.x, cs.y, cs.x + cx, cs.y + cy );
5262 /* check for wraparound */
5263 if (cs.x > 0x7fffffff - cx) rect.right = 0x7fffffff;
5264 if (cs.y > 0x7fffffff - cy) rect.bottom = 0x7fffffff;
5265 if (!apply_window_pos( hwnd, 0, SWP_NOZORDER | SWP_NOACTIVATE, &rect, &rect, NULL )) goto failed;
5267 /* send WM_NCCREATE */
5269 TRACE( "hwnd %p cs %d,%d %dx%d %s\n", hwnd, cs.x, cs.y, cs.cx, cs.cy, wine_dbgstr_rect(&rect) );
5270 if (!send_message_timeout( hwnd, WM_NCCREATE, 0, (LPARAM)&cs, SMTO_NORMAL, 0, ansi ))
5272 WARN( "%p: aborted by WM_NCCREATE\n", hwnd );
5273 goto failed;
5276 /* create default IME window */
5278 if (!is_desktop_window( hwnd ) && parent != get_hwnd_message_parent() &&
5279 register_imm_window( hwnd ))
5281 TRACE( "register IME window for %p\n", hwnd );
5282 win_set_flags( hwnd, WIN_HAS_IME_WIN, 0 );
5285 /* send WM_NCCALCSIZE */
5287 if (get_window_rects( hwnd, COORDS_PARENT, &rect, NULL, win_dpi ))
5289 /* yes, even if the CBT hook was called with HWND_TOP */
5290 HWND insert_after = (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) ? HWND_BOTTOM : HWND_TOP;
5291 RECT client_rect = rect;
5293 /* the rectangle is in screen coords for WM_NCCALCSIZE when wparam is FALSE */
5294 map_window_points( parent, 0, (POINT *)&client_rect, 2, win_dpi );
5295 send_message( hwnd, WM_NCCALCSIZE, FALSE, (LPARAM)&client_rect );
5296 map_window_points( 0, parent, (POINT *)&client_rect, 2, win_dpi );
5297 apply_window_pos( hwnd, insert_after, SWP_NOACTIVATE, &rect, &client_rect, NULL );
5299 else goto failed;
5301 /* send WM_CREATE */
5302 if (send_message_timeout( hwnd, WM_CREATE, 0, (LPARAM)&cs, SMTO_NORMAL, 0, ansi ) == -1)
5303 goto failed;
5305 /* call the driver */
5307 if (!user_driver->pCreateWindow( hwnd )) goto failed;
5309 NtUserNotifyWinEvent( EVENT_OBJECT_CREATE, hwnd, OBJID_WINDOW, 0 );
5311 /* send the size messages */
5313 if (!(win_get_flags( hwnd ) & WIN_NEED_SIZE))
5315 get_window_rects( hwnd, COORDS_PARENT, NULL, &rect, win_dpi );
5316 send_message( hwnd, WM_SIZE, SIZE_RESTORED,
5317 MAKELONG(rect.right-rect.left, rect.bottom-rect.top));
5318 send_message( hwnd, WM_MOVE, 0, MAKELONG( rect.left, rect.top ) );
5321 /* Show the window, maximizing or minimizing if needed */
5323 style = set_window_style( hwnd, 0, WS_MAXIMIZE | WS_MINIMIZE );
5324 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
5326 RECT new_pos;
5327 UINT sw_flags = (style & WS_MINIMIZE) ? SW_MINIMIZE : SW_MAXIMIZE;
5329 sw_flags = window_min_maximize( hwnd, sw_flags, &new_pos );
5330 sw_flags |= SWP_FRAMECHANGED; /* Frame always gets changed */
5331 if (!(style & WS_VISIBLE) || (style & WS_CHILD) || get_active_window())
5332 sw_flags |= SWP_NOACTIVATE;
5333 NtUserSetWindowPos( hwnd, 0, new_pos.left, new_pos.top, new_pos.right - new_pos.left,
5334 new_pos.bottom - new_pos.top, sw_flags );
5337 /* Notify the parent window only */
5339 send_parent_notify( hwnd, WM_CREATE );
5340 if (!is_window( hwnd ))
5342 SetThreadDpiAwarenessContext( context );
5343 return 0;
5346 if (parent == get_desktop_window())
5347 NtUserPostMessage( parent, WM_PARENTNOTIFY, WM_CREATE, (LPARAM)hwnd );
5349 if (cs.style & WS_VISIBLE)
5351 if (cs.style & WS_MAXIMIZE)
5352 sw = SW_SHOW;
5353 else if (cs.style & WS_MINIMIZE)
5354 sw = SW_SHOWMINIMIZED;
5356 NtUserShowWindow( hwnd, sw );
5357 if (cs.dwExStyle & WS_EX_MDICHILD)
5359 send_message( cs.hwndParent, WM_MDIREFRESHMENU, 0, 0 );
5360 /* ShowWindow won't activate child windows */
5361 NtUserSetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE );
5365 /* Call WH_SHELL hook */
5367 if (!(get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) && !get_window_relative( hwnd, GW_OWNER ))
5368 call_hooks( WH_SHELL, HSHELL_WINDOWCREATED, (WPARAM)hwnd, 0, 0 );
5370 TRACE( "created window %p\n", hwnd );
5371 SetThreadDpiAwarenessContext( context );
5372 return hwnd;
5374 failed:
5375 destroy_window( hwnd );
5376 SetThreadDpiAwarenessContext( context );
5377 return 0;
5380 static void *get_dialog_info( HWND hwnd )
5382 WND *win;
5383 void *ret;
5385 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP)
5387 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
5388 return NULL;
5391 ret = win->dlgInfo;
5392 release_win_ptr( win );
5393 return ret;
5396 static BOOL set_dialog_info( HWND hwnd, void *info )
5398 WND *win;
5400 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
5401 win->dlgInfo = info;
5402 release_win_ptr( win );
5403 return TRUE;
5406 /*****************************************************************************
5407 * NtUserCallHwnd (win32u.@)
5409 ULONG_PTR WINAPI NtUserCallHwnd( HWND hwnd, DWORD code )
5411 switch (code)
5413 case NtUserCallHwnd_ActivateOtherWindow:
5414 activate_other_window( hwnd );
5415 return 0;
5417 case NtUserCallHwnd_ArrangeIconicWindows:
5418 return arrange_iconic_windows( hwnd );
5420 case NtUserCallHwnd_DrawMenuBar:
5421 return draw_menu_bar( hwnd );
5423 case NtUserCallHwnd_GetDefaultImeWindow:
5424 return HandleToUlong( get_default_ime_window( hwnd ));
5426 case NtUserCallHwnd_GetDpiForWindow:
5427 return get_dpi_for_window( hwnd );
5429 case NtUserCallHwnd_GetParent:
5430 return HandleToUlong( get_parent( hwnd ));
5432 case NtUserCallHwnd_GetDialogInfo:
5433 return (ULONG_PTR)get_dialog_info( hwnd );
5435 case NtUserCallHwnd_GetMDIClientInfo:
5436 if (!(win_get_flags( hwnd ) & WIN_ISMDICLIENT)) return 0;
5437 return get_window_long_ptr( hwnd, sizeof(void *), FALSE );
5439 case NtUserCallHwnd_GetWindowContextHelpId:
5440 return get_window_context_help_id( hwnd );
5442 case NtUserCallHwnd_GetWindowDpiAwarenessContext:
5443 return (ULONG_PTR)get_window_dpi_awareness_context( hwnd );
5445 case NtUserCallHwnd_GetWindowInputContext:
5446 return HandleToUlong( get_window_input_context( hwnd ));
5448 case NtUserCallHwnd_GetWindowSysSubMenu:
5449 return HandleToUlong( get_window_sys_sub_menu( hwnd ));
5451 case NtUserCallHwnd_GetWindowTextLength:
5452 return get_server_window_text( hwnd, NULL, 0 );
5454 case NtUserCallHwnd_IsWindow:
5455 return is_window( hwnd );
5457 case NtUserCallHwnd_IsWindowEnabled:
5458 return is_window_enabled( hwnd );
5460 case NtUserCallHwnd_IsWindowUnicode:
5461 return is_window_unicode( hwnd );
5463 case NtUserCallHwnd_IsWindowVisible:
5464 return is_window_visible( hwnd );
5466 case NtUserCallHwnd_SetForegroundWindow:
5467 return set_foreground_window( hwnd, FALSE );
5469 case NtUserCallHwnd_SetProgmanWindow:
5470 return HandleToUlong( set_progman_window( hwnd ));
5472 case NtUserCallHwnd_SetTaskmanWindow:
5473 return HandleToUlong( set_taskman_window( hwnd ));
5475 /* temporary exports */
5476 case NtUserGetFullWindowHandle:
5477 return HandleToUlong( get_full_window_handle( hwnd ));
5479 case NtUserIsCurrentProcessWindow:
5480 return HandleToUlong( is_current_process_window( hwnd ));
5482 case NtUserIsCurrentThreadWindow:
5483 return HandleToUlong( is_current_thread_window( hwnd ));
5485 default:
5486 FIXME( "invalid code %u\n", (int)code );
5487 return 0;
5491 /*****************************************************************************
5492 * NtUserCallHwndParam (win32u.@)
5494 ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code )
5496 switch (code)
5498 case NtUserCallHwndParam_ClientToScreen:
5499 return client_to_screen( hwnd, (POINT *)param );
5501 case NtUserCallHwndParam_EnableWindow:
5502 return enable_window( hwnd, param );
5504 case NtUserCallHwndParam_GetChildRect:
5505 return get_window_rects( hwnd, COORDS_PARENT, (RECT *)param, NULL, get_thread_dpi() );
5507 case NtUserCallHwndParam_GetClassLongA:
5508 return get_class_long( hwnd, param, TRUE );
5510 case NtUserCallHwndParam_GetClassLongW:
5511 return get_class_long( hwnd, param, FALSE );
5513 case NtUserCallHwndParam_GetClassLongPtrA:
5514 return get_class_long_ptr( hwnd, param, TRUE );
5516 case NtUserCallHwndParam_GetClassLongPtrW:
5517 return get_class_long_ptr( hwnd, param, FALSE );
5519 case NtUserCallHwndParam_GetClassWord:
5520 return get_class_word( hwnd, param );
5522 case NtUserCallHwndParam_GetClientRect:
5523 return get_client_rect( hwnd, (RECT *)param );
5525 case NtUserCallHwndParam_GetScrollInfo:
5527 struct get_scroll_info_params *params = (void *)param;
5528 return get_scroll_info( hwnd, params->bar, params->info );
5531 case NtUserCallHwndParam_GetWindowInfo:
5532 return get_window_info( hwnd, (WINDOWINFO *)param );
5534 case NtUserCallHwndParam_GetWindowLongA:
5535 return get_window_long_size( hwnd, param, sizeof(LONG), TRUE );
5537 case NtUserCallHwndParam_GetWindowLongW:
5538 return get_window_long( hwnd, param );
5540 case NtUserCallHwndParam_GetWindowLongPtrA:
5541 return get_window_long_ptr( hwnd, param, TRUE );
5543 case NtUserCallHwndParam_GetWindowLongPtrW:
5544 return get_window_long_ptr( hwnd, param, FALSE );
5546 case NtUserCallHwndParam_GetWindowRect:
5547 return get_window_rect( hwnd, (RECT *)param, get_thread_dpi() );
5549 case NtUserCallHwndParam_GetWindowRelative:
5550 return HandleToUlong( get_window_relative( hwnd, param ));
5552 case NtUserCallHwndParam_GetWindowThread:
5553 return get_window_thread( hwnd, (DWORD *)param );
5555 case NtUserCallHwndParam_GetWindowWord:
5556 return get_window_word( hwnd, param );
5558 case NtUserCallHwndParam_IsChild:
5559 return is_child( hwnd, UlongToHandle(param) );
5561 case NtUserCallHwndParam_KillSystemTimer:
5562 return kill_system_timer( hwnd, param );
5564 case NtUserCallHwndParam_MapWindowPoints:
5566 struct map_window_points_params *params = (void *)param;
5567 return map_window_points( hwnd, params->hwnd_to, params->points, params->count,
5568 get_thread_dpi() );
5571 case NtUserCallHwndParam_MirrorRgn:
5572 return mirror_window_region( hwnd, UlongToHandle(param) );
5574 case NtUserCallHwndParam_MonitorFromWindow:
5575 return HandleToUlong( monitor_from_window( hwnd, param, get_thread_dpi() ));
5577 case NtUserCallHwndParam_ScreenToClient:
5578 return screen_to_client( hwnd, (POINT *)param );
5580 case NtUserCallHwndParam_SetDialogInfo:
5581 return set_dialog_info( hwnd, (void *)param );
5583 case NtUserCallHwndParam_SetMDIClientInfo:
5584 NtUserSetWindowLongPtr( hwnd, sizeof(void *), param, FALSE );
5585 return win_set_flags( hwnd, WIN_ISMDICLIENT, 0 );
5587 case NtUserCallHwndParam_SetWindowContextHelpId:
5588 return set_window_context_help_id( hwnd, param );
5590 case NtUserCallHwndParam_ShowOwnedPopups:
5591 return show_owned_popups( hwnd, param );
5593 /* temporary exports */
5594 case NtUserSetWindowStyle:
5596 STYLESTRUCT *style = (void *)param;
5597 return set_window_style( hwnd, style->styleNew, style->styleOld );
5600 default:
5601 FIXME( "invalid code %u\n", (int)code );
5602 return 0;
5606 /*******************************************************************
5607 * NtUserDragDetect (win32u.@)
5609 BOOL WINAPI NtUserDragDetect( HWND hwnd, int x, int y )
5611 WORD width, height;
5612 RECT rect;
5613 MSG msg;
5615 TRACE( "%p (%d,%d)\n", hwnd, x, y );
5617 if (!(NtUserGetKeyState( VK_LBUTTON ) & 0x8000)) return FALSE;
5619 width = get_system_metrics( SM_CXDRAG );
5620 height = get_system_metrics( SM_CYDRAG );
5621 SetRect( &rect, x - width, y - height, x + width, y + height );
5623 NtUserSetCapture( hwnd );
5625 for (;;)
5627 while (NtUserPeekMessage( &msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE ))
5629 if (msg.message == WM_LBUTTONUP)
5631 release_capture();
5632 return FALSE;
5634 if (msg.message == WM_MOUSEMOVE)
5636 POINT tmp;
5637 tmp.x = (short)LOWORD( msg.lParam );
5638 tmp.y = (short)HIWORD( msg.lParam );
5639 if (!PtInRect( &rect, tmp ))
5641 release_capture();
5642 return TRUE;
5646 NtUserMsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, 0 );
5648 return FALSE;
5651 /*******************************************************************
5652 * NtUserDragObject (win32u.@)
5654 DWORD WINAPI NtUserDragObject( HWND parent, HWND hwnd, UINT fmt, ULONG_PTR data, HCURSOR cursor )
5656 FIXME( "%p, %p, %u, %#lx, %p stub!\n", parent, hwnd, fmt, data, cursor );
5658 return 0;
5662 HWND get_shell_window(void)
5664 HWND hwnd = 0;
5666 SERVER_START_REQ(set_global_windows)
5668 req->flags = 0;
5669 if (!wine_server_call_err(req))
5670 hwnd = wine_server_ptr_handle( reply->old_shell_window );
5672 SERVER_END_REQ;
5674 return hwnd;
5677 /***********************************************************************
5678 * NtUserSetShellWindowEx (win32u.@)
5680 BOOL WINAPI NtUserSetShellWindowEx( HWND shell, HWND list_view )
5682 BOOL ret;
5684 /* shell = Progman[Program Manager]
5685 * |-> SHELLDLL_DefView
5686 * list_view = | |-> SysListView32
5687 * | | |-> tooltips_class32
5688 * | |
5689 * | |-> SysHeader32
5691 * |-> ProxyTarget
5694 if (get_shell_window())
5695 return FALSE;
5697 if (get_window_long( shell, GWL_EXSTYLE ) & WS_EX_TOPMOST)
5698 return FALSE;
5700 if (list_view != shell && (get_window_long( list_view, GWL_EXSTYLE ) & WS_EX_TOPMOST))
5701 return FALSE;
5703 if (list_view && list_view != shell)
5704 NtUserSetWindowPos( list_view, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
5706 NtUserSetWindowPos( shell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
5708 SERVER_START_REQ(set_global_windows)
5710 req->flags = SET_GLOBAL_SHELL_WINDOWS;
5711 req->shell_window = wine_server_user_handle( shell );
5712 req->shell_listview = wine_server_user_handle( list_view );
5713 ret = !wine_server_call_err(req);
5715 SERVER_END_REQ;
5716 return ret;
5719 HWND get_progman_window(void)
5721 HWND ret = 0;
5723 SERVER_START_REQ(set_global_windows)
5725 req->flags = 0;
5726 if (!wine_server_call_err(req))
5727 ret = wine_server_ptr_handle( reply->old_progman_window );
5729 SERVER_END_REQ;
5730 return ret;
5733 HWND set_progman_window( HWND hwnd )
5735 SERVER_START_REQ(set_global_windows)
5737 req->flags = SET_GLOBAL_PROGMAN_WINDOW;
5738 req->progman_window = wine_server_user_handle( hwnd );
5739 if (wine_server_call_err( req )) hwnd = 0;
5741 SERVER_END_REQ;
5742 return hwnd;
5745 HWND get_taskman_window(void)
5747 HWND ret = 0;
5749 SERVER_START_REQ(set_global_windows)
5751 req->flags = 0;
5752 if (!wine_server_call_err(req))
5753 ret = wine_server_ptr_handle( reply->old_taskman_window );
5755 SERVER_END_REQ;
5756 return ret;
5759 HWND set_taskman_window( HWND hwnd )
5761 /* hwnd = MSTaskSwWClass
5762 * |-> SysTabControl32
5764 SERVER_START_REQ(set_global_windows)
5766 req->flags = SET_GLOBAL_TASKMAN_WINDOW;
5767 req->taskman_window = wine_server_user_handle( hwnd );
5768 if (wine_server_call_err( req )) hwnd = 0;
5770 SERVER_END_REQ;
5771 return hwnd;