cmd: DIR command outputs free space for the path.
[wine.git] / dlls / win32u / window.c
blobb3047e7299a7a35c79cab70ae9246a225d3b0544
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;
2233 if (!hwnd) hwnd = get_desktop_window();
2235 *hittest = HTNOWHERE;
2237 if (!(list = list_children_from_point( hwnd, pt ))) return 0;
2239 /* now determine the hittest */
2241 for (i = 0; list[i]; i++)
2243 LONG style = get_window_long( list[i], GWL_STYLE );
2245 /* If window is minimized or disabled, return at once */
2246 if (style & WS_DISABLED)
2248 *hittest = HTERROR;
2249 break;
2251 /* Send WM_NCCHITTEST (if same thread) */
2252 if (!is_current_thread_window( list[i] ))
2254 *hittest = HTCLIENT;
2255 break;
2257 win_pt = map_dpi_point( pt, get_thread_dpi(), get_dpi_for_window( list[i] ));
2258 res = send_message( list[i], WM_NCHITTEST, 0, MAKELPARAM( win_pt.x, win_pt.y ));
2259 if (res != HTTRANSPARENT)
2261 *hittest = res; /* Found the window */
2262 break;
2264 /* continue search with next window in z-order */
2266 ret = list[i];
2267 free( list );
2268 TRACE( "scope %p (%d,%d) returning %p\n", hwnd, (int)pt.x, (int)pt.y, ret );
2269 return ret;
2272 /*******************************************************************
2273 * NtUserWindowFromPoint (win32u.@)
2275 HWND WINAPI NtUserWindowFromPoint( LONG x, LONG y )
2277 POINT pt = { .x = x, .y = y };
2278 INT hittest;
2279 return window_from_point( 0, pt, &hittest );
2282 /*******************************************************************
2283 * NtUserChildWindowFromPointEx (win32u.@)
2285 HWND WINAPI NtUserChildWindowFromPointEx( HWND parent, LONG x, LONG y, UINT flags )
2287 POINT pt = { .x = x, .y = y }; /* in the client coordinates */
2288 HWND *list;
2289 int i;
2290 RECT rect;
2291 HWND ret;
2293 get_client_rect( parent, &rect );
2294 if (!PtInRect( &rect, pt )) return 0;
2295 if (!(list = list_window_children( 0, parent, NULL, 0 ))) return parent;
2297 for (i = 0; list[i]; i++)
2299 if (!get_window_rects( list[i], COORDS_PARENT, &rect, NULL, get_thread_dpi() )) continue;
2300 if (!PtInRect( &rect, pt )) continue;
2301 if (flags & (CWP_SKIPINVISIBLE|CWP_SKIPDISABLED))
2303 LONG style = get_window_long( list[i], GWL_STYLE );
2304 if ((flags & CWP_SKIPINVISIBLE) && !(style & WS_VISIBLE)) continue;
2305 if ((flags & CWP_SKIPDISABLED) && (style & WS_DISABLED)) continue;
2307 if (flags & CWP_SKIPTRANSPARENT)
2309 if (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TRANSPARENT) continue;
2311 break;
2313 ret = list[i];
2314 free( list );
2315 if (!ret) ret = parent;
2316 return ret;
2319 /*******************************************************************
2320 * NtUserRealChildWindowFromPoint (win32u.@)
2322 HWND WINAPI NtUserRealChildWindowFromPoint( HWND parent, LONG x, LONG y )
2324 return NtUserChildWindowFromPointEx( parent, x, y, CWP_SKIPTRANSPARENT | CWP_SKIPINVISIBLE );
2327 /*******************************************************************
2328 * get_work_rect
2330 * Get the work area that a maximized window can cover, depending on style.
2332 static BOOL get_work_rect( HWND hwnd, RECT *rect )
2334 HMONITOR monitor = monitor_from_window( hwnd, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
2335 MONITORINFO mon_info;
2336 DWORD style;
2338 if (!monitor) return FALSE;
2340 mon_info.cbSize = sizeof(mon_info);
2341 get_monitor_info( monitor, &mon_info );
2342 *rect = mon_info.rcMonitor;
2344 style = get_window_long( hwnd, GWL_STYLE );
2345 if (style & WS_MAXIMIZEBOX)
2347 if ((style & WS_CAPTION) == WS_CAPTION || !(style & (WS_CHILD | WS_POPUP)))
2348 *rect = mon_info.rcWork;
2350 return TRUE;
2353 static RECT get_maximized_work_rect( HWND hwnd )
2355 RECT work_rect = { 0 };
2357 if ((get_window_long( hwnd, GWL_STYLE ) & (WS_MINIMIZE | WS_MAXIMIZE)) == WS_MAXIMIZE)
2359 if (!get_work_rect( hwnd, &work_rect ))
2360 work_rect = get_primary_monitor_rect( get_thread_dpi() );
2362 return work_rect;
2365 /*******************************************************************
2366 * update_maximized_pos
2368 * For top level windows covering the work area, we might have to
2369 * "forget" the maximized position. Windows presumably does this
2370 * to avoid situations where the border style changes, which would
2371 * lead the window to be outside the screen, or the window gets
2372 * reloaded on a different screen, and the "saved" position no
2373 * longer applies to it (despite being maximized).
2375 * Some applications (e.g. Imperiums: Greek Wars) depend on this.
2377 static void update_maximized_pos( WND *wnd, RECT *work_rect )
2379 if (wnd->parent && wnd->parent != get_desktop_window())
2380 return;
2382 if (wnd->dwStyle & WS_MAXIMIZE)
2384 if (wnd->window_rect.left <= work_rect->left && wnd->window_rect.top <= work_rect->top &&
2385 wnd->window_rect.right >= work_rect->right && wnd->window_rect.bottom >= work_rect->bottom)
2386 wnd->max_pos.x = wnd->max_pos.y = -1;
2388 else
2389 wnd->max_pos.x = wnd->max_pos.y = -1;
2392 static BOOL empty_point( POINT pt )
2394 return pt.x == -1 && pt.y == -1;
2397 /***********************************************************************
2398 * NtUserGetWindowPlacement (win32u.@)
2400 BOOL WINAPI NtUserGetWindowPlacement( HWND hwnd, WINDOWPLACEMENT *placement )
2402 RECT work_rect = get_maximized_work_rect( hwnd );
2403 WND *win = get_win_ptr( hwnd );
2404 UINT win_dpi;
2406 if (!win) return FALSE;
2408 if (win == WND_DESKTOP)
2410 placement->length = sizeof(*placement);
2411 placement->showCmd = SW_SHOWNORMAL;
2412 placement->flags = 0;
2413 placement->ptMinPosition.x = -1;
2414 placement->ptMinPosition.y = -1;
2415 placement->ptMaxPosition.x = -1;
2416 placement->ptMaxPosition.y = -1;
2417 get_window_rect( hwnd, &placement->rcNormalPosition, get_thread_dpi() );
2418 return TRUE;
2420 if (win == WND_OTHER_PROCESS)
2422 RECT normal_position;
2423 DWORD style;
2425 if (!get_window_rect( hwnd, &normal_position, get_thread_dpi() ))
2426 return FALSE;
2428 FIXME("not fully supported on other process window %p.\n", hwnd);
2430 placement->length = sizeof(*placement);
2431 style = get_window_long( hwnd, GWL_STYLE );
2432 if (style & WS_MINIMIZE)
2433 placement->showCmd = SW_SHOWMINIMIZED;
2434 else
2435 placement->showCmd = (style & WS_MAXIMIZE) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL;
2436 /* provide some dummy information */
2437 placement->flags = 0;
2438 placement->ptMinPosition.x = -1;
2439 placement->ptMinPosition.y = -1;
2440 placement->ptMaxPosition.x = -1;
2441 placement->ptMaxPosition.y = -1;
2442 placement->rcNormalPosition = normal_position;
2443 return TRUE;
2446 /* update the placement according to the current style */
2447 if (win->dwStyle & WS_MINIMIZE)
2449 win->min_pos.x = win->window_rect.left;
2450 win->min_pos.y = win->window_rect.top;
2452 else if (win->dwStyle & WS_MAXIMIZE)
2454 win->max_pos.x = win->window_rect.left;
2455 win->max_pos.y = win->window_rect.top;
2457 else
2459 win->normal_rect = win->window_rect;
2461 update_maximized_pos( win, &work_rect );
2463 placement->length = sizeof(*placement);
2464 if (win->dwStyle & WS_MINIMIZE)
2465 placement->showCmd = SW_SHOWMINIMIZED;
2466 else
2467 placement->showCmd = ( win->dwStyle & WS_MAXIMIZE ) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL ;
2468 if (win->flags & WIN_RESTORE_MAX)
2469 placement->flags = WPF_RESTORETOMAXIMIZED;
2470 else
2471 placement->flags = 0;
2472 win_dpi = get_dpi_for_window( hwnd );
2473 placement->ptMinPosition = empty_point(win->min_pos) ? win->min_pos
2474 : map_dpi_point( win->min_pos, win_dpi, get_thread_dpi() );
2475 placement->ptMaxPosition = empty_point(win->max_pos) ? win->max_pos
2476 : map_dpi_point( win->max_pos, win_dpi, get_thread_dpi() );
2477 placement->rcNormalPosition = map_dpi_rect( win->normal_rect, win_dpi, get_thread_dpi() );
2478 release_win_ptr( win );
2480 TRACE( "%p: returning min %d,%d max %d,%d normal %s\n",
2481 hwnd, (int)placement->ptMinPosition.x, (int)placement->ptMinPosition.y,
2482 (int)placement->ptMaxPosition.x, (int)placement->ptMaxPosition.y,
2483 wine_dbgstr_rect(&placement->rcNormalPosition) );
2484 return TRUE;
2487 /* make sure the specified rect is visible on screen */
2488 static void make_rect_onscreen( RECT *rect )
2490 MONITORINFO info;
2491 HMONITOR monitor = monitor_from_rect( rect, MONITOR_DEFAULTTONEAREST, get_thread_dpi() );
2493 info.cbSize = sizeof(info);
2494 if (!monitor || !get_monitor_info( monitor, &info )) return;
2495 /* FIXME: map coordinates from rcWork to rcMonitor */
2496 if (rect->right <= info.rcWork.left)
2498 rect->right += info.rcWork.left - rect->left;
2499 rect->left = info.rcWork.left;
2501 else if (rect->left >= info.rcWork.right)
2503 rect->left += info.rcWork.right - rect->right;
2504 rect->right = info.rcWork.right;
2506 if (rect->bottom <= info.rcWork.top)
2508 rect->bottom += info.rcWork.top - rect->top;
2509 rect->top = info.rcWork.top;
2511 else if (rect->top >= info.rcWork.bottom)
2513 rect->top += info.rcWork.bottom - rect->bottom;
2514 rect->bottom = info.rcWork.bottom;
2518 /***********************************************************************
2519 * NtUserGetInternalWindowPos (win32u.@)
2521 UINT WINAPI NtUserGetInternalWindowPos( HWND hwnd, RECT *rect, POINT *pt )
2523 WINDOWPLACEMENT placement;
2525 placement.length = sizeof(placement);
2526 if (!NtUserGetWindowPlacement( hwnd, &placement )) return 0;
2527 if (rect) *rect = placement.rcNormalPosition;
2528 if (pt) *pt = placement.ptMinPosition;
2529 return placement.showCmd;
2532 /* make sure the specified point is visible on screen */
2533 static void make_point_onscreen( POINT *pt )
2535 RECT rect;
2537 SetRect( &rect, pt->x, pt->y, pt->x + 1, pt->y + 1 );
2538 make_rect_onscreen( &rect );
2539 pt->x = rect.left;
2540 pt->y = rect.top;
2543 static BOOL set_window_placement( HWND hwnd, const WINDOWPLACEMENT *wndpl, UINT flags )
2545 RECT work_rect = get_maximized_work_rect( hwnd );
2546 WND *win = get_win_ptr( hwnd );
2547 WINDOWPLACEMENT wp = *wndpl;
2548 DWORD style;
2550 if (flags & PLACE_MIN) make_point_onscreen( &wp.ptMinPosition );
2551 if (flags & PLACE_MAX) make_point_onscreen( &wp.ptMaxPosition );
2552 if (flags & PLACE_RECT) make_rect_onscreen( &wp.rcNormalPosition );
2554 TRACE( "%p: setting min %d,%d max %d,%d normal %s flags %x adjusted to min %d,%d max %d,%d normal %s\n",
2555 hwnd, (int)wndpl->ptMinPosition.x, (int)wndpl->ptMinPosition.y,
2556 (int)wndpl->ptMaxPosition.x, (int)wndpl->ptMaxPosition.y,
2557 wine_dbgstr_rect(&wndpl->rcNormalPosition), flags,
2558 (int)wp.ptMinPosition.x, (int)wp.ptMinPosition.y,
2559 (int)wp.ptMaxPosition.x, (int)wp.ptMaxPosition.y,
2560 wine_dbgstr_rect(&wp.rcNormalPosition) );
2562 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
2564 if (flags & PLACE_MIN) win->min_pos = point_thread_to_win_dpi( hwnd, wp.ptMinPosition );
2565 if (flags & PLACE_MAX)
2567 win->max_pos = point_thread_to_win_dpi( hwnd, wp.ptMaxPosition );
2568 update_maximized_pos( win, &work_rect );
2570 if (flags & PLACE_RECT) win->normal_rect = rect_thread_to_win_dpi( hwnd, wp.rcNormalPosition );
2572 style = win->dwStyle;
2574 release_win_ptr( win );
2576 if (style & WS_MINIMIZE)
2578 if (flags & PLACE_MIN)
2580 NtUserSetWindowPos( hwnd, 0, wp.ptMinPosition.x, wp.ptMinPosition.y, 0, 0,
2581 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
2584 else if (style & WS_MAXIMIZE)
2586 if (flags & PLACE_MAX)
2587 NtUserSetWindowPos( hwnd, 0, wp.ptMaxPosition.x, wp.ptMaxPosition.y, 0, 0,
2588 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
2590 else if (flags & PLACE_RECT)
2591 NtUserSetWindowPos( hwnd, 0, wp.rcNormalPosition.left, wp.rcNormalPosition.top,
2592 wp.rcNormalPosition.right - wp.rcNormalPosition.left,
2593 wp.rcNormalPosition.bottom - wp.rcNormalPosition.top,
2594 SWP_NOZORDER | SWP_NOACTIVATE );
2596 NtUserShowWindow( hwnd, wndpl->showCmd );
2598 if (is_iconic( hwnd ))
2600 if (wndpl->flags & WPF_RESTORETOMAXIMIZED)
2601 win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
2603 return TRUE;
2606 /***********************************************************************
2607 * NtUserSetWindowPlacement (win32u.@)
2609 BOOL WINAPI NtUserSetWindowPlacement( HWND hwnd, const WINDOWPLACEMENT *wpl )
2611 UINT flags = PLACE_MAX | PLACE_RECT;
2612 if (!wpl) return FALSE;
2613 if (wpl->length != sizeof(*wpl))
2615 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
2616 return FALSE;
2618 if (wpl->flags & WPF_SETMINPOSITION) flags |= PLACE_MIN;
2619 return set_window_placement( hwnd, wpl, flags );
2622 /*****************************************************************************
2623 * NtUserBuildHwndList (win32u.@)
2625 NTSTATUS WINAPI NtUserBuildHwndList( HDESK desktop, ULONG unk2, ULONG unk3, ULONG unk4,
2626 ULONG thread_id, ULONG count, HWND *buffer, ULONG *size )
2628 user_handle_t *list = (user_handle_t *)buffer;
2629 int i;
2630 NTSTATUS status;
2632 SERVER_START_REQ( get_window_children )
2634 req->desktop = wine_server_obj_handle( desktop );
2635 req->tid = thread_id;
2636 if (count) wine_server_set_reply( req, list, (count - 1) * sizeof(user_handle_t) );
2637 status = wine_server_call( req );
2638 if (status && status != STATUS_BUFFER_TOO_SMALL) return status;
2639 *size = reply->count + 1;
2641 SERVER_END_REQ;
2642 if (*size > count) return STATUS_BUFFER_TOO_SMALL;
2644 /* start from the end since HWND is potentially larger than user_handle_t */
2645 for (i = *size - 2; i >= 0; i--)
2646 buffer[i] = wine_server_ptr_handle( list[i] );
2647 buffer[*size - 1] = HWND_BOTTOM;
2648 return STATUS_SUCCESS;
2651 /***********************************************************************
2652 * NtUserFindWindowEx (USER32.@)
2654 HWND WINAPI NtUserFindWindowEx( HWND parent, HWND child, UNICODE_STRING *class, UNICODE_STRING *title,
2655 ULONG unk )
2657 HWND *list;
2658 HWND retvalue = 0;
2659 int i = 0, len = 0, title_len;
2660 WCHAR *buffer = NULL;
2662 if (!parent && child) parent = get_desktop_window();
2663 else if (parent == HWND_MESSAGE) parent = get_hwnd_message_parent();
2665 if (title)
2667 len = title->Length / sizeof(WCHAR) + 1; /* one extra char to check for chars beyond the end */
2668 if (!(buffer = malloc( (len + 1) * sizeof(WCHAR) ))) return 0;
2671 if (!(list = list_window_children( 0, parent, class, 0 ))) goto done;
2673 if (child)
2675 child = get_full_window_handle( child );
2676 while (list[i] && list[i] != child) i++;
2677 if (!list[i]) goto done;
2678 i++; /* start from next window */
2681 if (title)
2683 while (list[i])
2685 title_len = NtUserInternalGetWindowText( list[i], buffer, len + 1 );
2686 if (title_len * sizeof(WCHAR) == title->Length &&
2687 (!title_len || !wcsnicmp( buffer, title->Buffer, title_len )))
2688 break;
2689 i++;
2692 retvalue = list[i];
2694 done:
2695 free( list );
2696 free( buffer );
2697 return retvalue;
2700 /* Retrieve the window text from the server. */
2701 static data_size_t get_server_window_text( HWND hwnd, WCHAR *text, data_size_t count )
2703 data_size_t len = 0, needed = 0;
2705 SERVER_START_REQ( get_window_text )
2707 req->handle = wine_server_user_handle( hwnd );
2708 if (count) wine_server_set_reply( req, text, (count - 1) * sizeof(WCHAR) );
2709 if (!wine_server_call_err( req ))
2711 needed = reply->length;
2712 len = wine_server_reply_size(reply);
2715 SERVER_END_REQ;
2716 if (text) text[len / sizeof(WCHAR)] = 0;
2717 return needed;
2720 /*******************************************************************
2721 * NtUserInternalGetWindowText (win32u.@)
2723 INT WINAPI NtUserInternalGetWindowText( HWND hwnd, WCHAR *text, INT count )
2725 WND *win;
2727 if (count <= 0) return 0;
2728 if (!(win = get_win_ptr( hwnd ))) return 0;
2729 if (win == WND_DESKTOP) text[0] = 0;
2730 else if (win != WND_OTHER_PROCESS)
2732 if (win->text) lstrcpynW( text, win->text, count );
2733 else text[0] = 0;
2734 release_win_ptr( win );
2736 else
2738 get_server_window_text( hwnd, text, count );
2740 return lstrlenW(text);
2743 /*******************************************************************
2744 * get_windows_offset
2746 * Calculate the offset between the origin of the two windows. Used
2747 * to implement MapWindowPoints.
2749 static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, UINT dpi, BOOL *mirrored, POINT *ret_offset )
2751 WND *win;
2752 POINT offset;
2753 BOOL mirror_from, mirror_to, ret;
2754 HWND hwnd;
2756 offset.x = offset.y = 0;
2757 *mirrored = mirror_from = mirror_to = FALSE;
2759 /* Translate source window origin to screen coords */
2760 if (hwnd_from)
2762 if (!(win = get_win_ptr( hwnd_from )))
2764 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2765 return FALSE;
2767 if (win == WND_OTHER_PROCESS) goto other_process;
2768 if (win != WND_DESKTOP)
2770 if (win->dwExStyle & WS_EX_LAYOUTRTL)
2772 mirror_from = TRUE;
2773 offset.x += win->client_rect.right - win->client_rect.left;
2775 while (win->parent)
2777 offset.x += win->client_rect.left;
2778 offset.y += win->client_rect.top;
2779 hwnd = win->parent;
2780 release_win_ptr( win );
2781 if (!(win = get_win_ptr( hwnd ))) break;
2782 if (win == WND_OTHER_PROCESS) goto other_process;
2783 if (win == WND_DESKTOP) break;
2784 if (win->flags & WIN_CHILDREN_MOVED)
2786 release_win_ptr( win );
2787 goto other_process;
2790 if (win && win != WND_DESKTOP) release_win_ptr( win );
2791 offset = map_dpi_point( offset, get_dpi_for_window( hwnd_from ), dpi );
2795 /* Translate origin to destination window coords */
2796 if (hwnd_to)
2798 if (!(win = get_win_ptr( hwnd_to )))
2800 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2801 return FALSE;
2803 if (win == WND_OTHER_PROCESS) goto other_process;
2804 if (win != WND_DESKTOP)
2806 POINT pt = { 0, 0 };
2807 if (win->dwExStyle & WS_EX_LAYOUTRTL)
2809 mirror_to = TRUE;
2810 pt.x += win->client_rect.right - win->client_rect.left;
2812 while (win->parent)
2814 pt.x += win->client_rect.left;
2815 pt.y += win->client_rect.top;
2816 hwnd = win->parent;
2817 release_win_ptr( win );
2818 if (!(win = get_win_ptr( hwnd ))) break;
2819 if (win == WND_OTHER_PROCESS) goto other_process;
2820 if (win == WND_DESKTOP) break;
2821 if (win->flags & WIN_CHILDREN_MOVED)
2823 release_win_ptr( win );
2824 goto other_process;
2827 if (win && win != WND_DESKTOP) release_win_ptr( win );
2828 pt = map_dpi_point( pt, get_dpi_for_window( hwnd_to ), dpi );
2829 offset.x -= pt.x;
2830 offset.y -= pt.y;
2834 *mirrored = mirror_from ^ mirror_to;
2835 if (mirror_from) offset.x = -offset.x;
2836 *ret_offset = offset;
2837 return TRUE;
2839 other_process: /* one of the parents may belong to another process, do it the hard way */
2840 SERVER_START_REQ( get_windows_offset )
2842 req->from = wine_server_user_handle( hwnd_from );
2843 req->to = wine_server_user_handle( hwnd_to );
2844 req->dpi = dpi;
2845 if ((ret = !wine_server_call_err( req )))
2847 ret_offset->x = reply->x;
2848 ret_offset->y = reply->y;
2849 *mirrored = reply->mirror;
2852 SERVER_END_REQ;
2853 return ret;
2856 /* see ClientToScreen */
2857 BOOL client_to_screen( HWND hwnd, POINT *pt )
2859 POINT offset;
2860 BOOL mirrored;
2862 if (!hwnd)
2864 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2865 return FALSE;
2868 if (!get_windows_offset( hwnd, 0, get_thread_dpi(), &mirrored, &offset )) return FALSE;
2869 pt->x += offset.x;
2870 pt->y += offset.y;
2871 if (mirrored) pt->x = -pt->x;
2872 return TRUE;
2875 /* see ScreenToClient */
2876 BOOL screen_to_client( HWND hwnd, POINT *pt )
2878 POINT offset;
2879 BOOL mirrored;
2881 if (!hwnd)
2883 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2884 return FALSE;
2886 if (!get_windows_offset( 0, hwnd, get_thread_dpi(), &mirrored, &offset )) return FALSE;
2887 pt->x += offset.x;
2888 pt->y += offset.y;
2889 if (mirrored) pt->x = -pt->x;
2890 return TRUE;
2893 /* map coordinates of a window region */
2894 void map_window_region( HWND from, HWND to, HRGN hrgn )
2896 BOOL mirrored;
2897 POINT offset;
2898 UINT i, size;
2899 RGNDATA *data;
2900 HRGN new_rgn;
2901 RECT *rect;
2903 if (!get_windows_offset( from, to, get_thread_dpi(), &mirrored, &offset )) return;
2905 if (!mirrored)
2907 NtGdiOffsetRgn( hrgn, offset.x, offset.y );
2908 return;
2910 if (!(size = NtGdiGetRegionData( hrgn, 0, NULL ))) return;
2911 if (!(data = malloc( size ))) return;
2912 NtGdiGetRegionData( hrgn, size, data );
2913 rect = (RECT *)data->Buffer;
2914 for (i = 0; i < data->rdh.nCount; i++)
2916 int tmp = -(rect[i].left + offset.x);
2917 rect[i].left = -(rect[i].right + offset.x);
2918 rect[i].right = tmp;
2919 rect[i].top += offset.y;
2920 rect[i].bottom += offset.y;
2922 if ((new_rgn = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data )))
2924 NtGdiCombineRgn( hrgn, new_rgn, 0, RGN_COPY );
2925 NtGdiDeleteObjectApp( new_rgn );
2927 free( data );
2930 /* see MapWindowPoints */
2931 int map_window_points( HWND hwnd_from, HWND hwnd_to, POINT *points, UINT count, UINT dpi )
2933 BOOL mirrored;
2934 POINT offset;
2935 UINT i;
2937 if (!get_windows_offset( hwnd_from, hwnd_to, dpi, &mirrored, &offset )) return 0;
2939 for (i = 0; i < count; i++)
2941 points[i].x += offset.x;
2942 points[i].y += offset.y;
2943 if (mirrored) points[i].x = -points[i].x;
2945 if (mirrored && count == 2) /* special case for rectangle */
2947 int tmp = points[0].x;
2948 points[0].x = points[1].x;
2949 points[1].x = tmp;
2951 return MAKELONG( LOWORD(offset.x), LOWORD(offset.y) );
2954 /***********************************************************************
2955 * dump_winpos_flags
2957 static void dump_winpos_flags( UINT flags )
2959 static const UINT dumped_flags = (SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW |
2960 SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_SHOWWINDOW |
2961 SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOOWNERZORDER |
2962 SWP_NOSENDCHANGING | SWP_DEFERERASE | SWP_ASYNCWINDOWPOS |
2963 SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE | SWP_STATECHANGED);
2964 TRACE( "flags:" );
2965 if(flags & SWP_NOSIZE) TRACE( " SWP_NOSIZE" );
2966 if(flags & SWP_NOMOVE) TRACE( " SWP_NOMOVE" );
2967 if(flags & SWP_NOZORDER) TRACE( " SWP_NOZORDER" );
2968 if(flags & SWP_NOREDRAW) TRACE( " SWP_NOREDRAW" );
2969 if(flags & SWP_NOACTIVATE) TRACE( " SWP_NOACTIVATE" );
2970 if(flags & SWP_FRAMECHANGED) TRACE( " SWP_FRAMECHANGED" );
2971 if(flags & SWP_SHOWWINDOW) TRACE( " SWP_SHOWWINDOW" );
2972 if(flags & SWP_HIDEWINDOW) TRACE( " SWP_HIDEWINDOW" );
2973 if(flags & SWP_NOCOPYBITS) TRACE( " SWP_NOCOPYBITS" );
2974 if(flags & SWP_NOOWNERZORDER) TRACE( " SWP_NOOWNERZORDER" );
2975 if(flags & SWP_NOSENDCHANGING) TRACE( " SWP_NOSENDCHANGING" );
2976 if(flags & SWP_DEFERERASE) TRACE( " SWP_DEFERERASE" );
2977 if(flags & SWP_ASYNCWINDOWPOS) TRACE( " SWP_ASYNCWINDOWPOS" );
2978 if(flags & SWP_NOCLIENTSIZE) TRACE( " SWP_NOCLIENTSIZE" );
2979 if(flags & SWP_NOCLIENTMOVE) TRACE( " SWP_NOCLIENTMOVE" );
2980 if(flags & SWP_STATECHANGED) TRACE( " SWP_STATECHANGED" );
2982 if(flags & ~dumped_flags) TRACE( " %08x", flags & ~dumped_flags );
2983 TRACE( "\n" );
2986 /***********************************************************************
2987 * map_dpi_winpos
2989 static void map_dpi_winpos( WINDOWPOS *winpos )
2991 UINT dpi_from = get_thread_dpi();
2992 UINT dpi_to = get_dpi_for_window( winpos->hwnd );
2994 if (!dpi_from) dpi_from = get_win_monitor_dpi( winpos->hwnd );
2995 if (dpi_from == dpi_to) return;
2996 winpos->x = muldiv( winpos->x, dpi_to, dpi_from );
2997 winpos->y = muldiv( winpos->y, dpi_to, dpi_from );
2998 winpos->cx = muldiv( winpos->cx, dpi_to, dpi_from );
2999 winpos->cy = muldiv( winpos->cy, dpi_to, dpi_from );
3002 /***********************************************************************
3003 * calc_winpos
3005 static BOOL calc_winpos( WINDOWPOS *winpos, RECT *old_window_rect, RECT *old_client_rect,
3006 RECT *new_window_rect, RECT *new_client_rect )
3008 WND *win;
3010 /* Send WM_WINDOWPOSCHANGING message */
3011 if (!(winpos->flags & SWP_NOSENDCHANGING)
3012 && !((winpos->flags & SWP_AGG_NOCLIENTCHANGE) && (winpos->flags & SWP_SHOWWINDOW)))
3013 send_message( winpos->hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)winpos );
3015 if (!(win = get_win_ptr( winpos->hwnd )) ||
3016 win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
3018 /* Calculate new position and size */
3019 get_window_rects( winpos->hwnd, COORDS_PARENT, old_window_rect, old_client_rect, get_thread_dpi() );
3020 *new_window_rect = *old_window_rect;
3021 *new_client_rect = *old_client_rect;
3023 if (!(winpos->flags & SWP_NOSIZE))
3025 if (win->dwStyle & WS_MINIMIZE)
3027 new_window_rect->right = new_window_rect->left + get_system_metrics( SM_CXMINIMIZED );
3028 new_window_rect->bottom = new_window_rect->top + get_system_metrics( SM_CYMINIMIZED );
3030 else
3032 new_window_rect->right = new_window_rect->left + winpos->cx;
3033 new_window_rect->bottom = new_window_rect->top + winpos->cy;
3037 if (!(winpos->flags & SWP_NOMOVE))
3039 /* If the window is toplevel minimized off-screen, force keep it there */
3040 if ((win->dwStyle & WS_MINIMIZE) &&
3041 win->window_rect.left <= -32000 && win->window_rect.top <= -32000 &&
3042 (!win->parent || win->parent == get_desktop_window()))
3044 winpos->x = -32000;
3045 winpos->y = -32000;
3047 new_window_rect->left = winpos->x;
3048 new_window_rect->top = winpos->y;
3049 new_window_rect->right += winpos->x - old_window_rect->left;
3050 new_window_rect->bottom += winpos->y - old_window_rect->top;
3052 OffsetRect( new_client_rect, winpos->x - old_window_rect->left,
3053 winpos->y - old_window_rect->top );
3055 winpos->flags |= SWP_NOCLIENTMOVE | SWP_NOCLIENTSIZE;
3057 TRACE( "hwnd %p, after %p, swp %d,%d %dx%d flags %08x current %s style %08x new %s\n",
3058 winpos->hwnd, winpos->hwndInsertAfter, winpos->x, winpos->y,
3059 winpos->cx, winpos->cy, winpos->flags,
3060 wine_dbgstr_rect( old_window_rect ), win->dwStyle,
3061 wine_dbgstr_rect( new_window_rect ));
3063 release_win_ptr( win );
3064 return TRUE;
3067 /***********************************************************************
3068 * get_valid_rects
3070 * Compute the valid rects from the old and new client rect and WVR_* flags.
3071 * Helper for WM_NCCALCSIZE handling.
3073 static inline void get_valid_rects( const RECT *old_client, const RECT *new_client, UINT flags,
3074 RECT *valid )
3076 int cx, cy;
3078 if (flags & WVR_REDRAW)
3080 SetRectEmpty( &valid[0] );
3081 SetRectEmpty( &valid[1] );
3082 return;
3085 if (flags & WVR_VALIDRECTS)
3087 if (!intersect_rect( &valid[0], &valid[0], new_client ) ||
3088 !intersect_rect( &valid[1], &valid[1], old_client ))
3090 SetRectEmpty( &valid[0] );
3091 SetRectEmpty( &valid[1] );
3092 return;
3094 flags = WVR_ALIGNLEFT | WVR_ALIGNTOP;
3096 else
3098 valid[0] = *new_client;
3099 valid[1] = *old_client;
3102 /* make sure the rectangles have the same size */
3103 cx = min( valid[0].right - valid[0].left, valid[1].right - valid[1].left );
3104 cy = min( valid[0].bottom - valid[0].top, valid[1].bottom - valid[1].top );
3106 if (flags & WVR_ALIGNBOTTOM)
3108 valid[0].top = valid[0].bottom - cy;
3109 valid[1].top = valid[1].bottom - cy;
3111 else
3113 valid[0].bottom = valid[0].top + cy;
3114 valid[1].bottom = valid[1].top + cy;
3116 if (flags & WVR_ALIGNRIGHT)
3118 valid[0].left = valid[0].right - cx;
3119 valid[1].left = valid[1].right - cx;
3121 else
3123 valid[0].right = valid[0].left + cx;
3124 valid[1].right = valid[1].left + cx;
3128 static UINT calc_ncsize( WINDOWPOS *winpos, const RECT *old_window_rect, const RECT *old_client_rect,
3129 const RECT *new_window_rect, RECT *new_client_rect, RECT *valid_rects,
3130 int parent_x, int parent_y )
3132 UINT wvr_flags = 0;
3134 /* Send WM_NCCALCSIZE message to get new client area */
3135 if ((winpos->flags & (SWP_FRAMECHANGED | SWP_NOSIZE)) != SWP_NOSIZE)
3137 NCCALCSIZE_PARAMS params;
3138 WINDOWPOS winposCopy;
3139 UINT class_style;
3141 params.rgrc[0] = *new_window_rect;
3142 params.rgrc[1] = *old_window_rect;
3143 params.rgrc[2] = *old_client_rect;
3144 params.lppos = &winposCopy;
3145 winposCopy = *winpos;
3147 if (winpos->flags & SWP_NOMOVE)
3149 winposCopy.x = old_window_rect->left;
3150 winposCopy.y = old_window_rect->top;
3153 if (winpos->flags & SWP_NOSIZE)
3155 winposCopy.cx = old_window_rect->right - old_window_rect->left;
3156 winposCopy.cy = old_window_rect->bottom - old_window_rect->top;
3159 class_style = get_class_long( winpos->hwnd, GCL_STYLE, FALSE );
3160 if (class_style & CS_VREDRAW) wvr_flags |= WVR_VREDRAW;
3161 if (class_style & CS_HREDRAW) wvr_flags |= WVR_HREDRAW;
3163 wvr_flags |= send_message( winpos->hwnd, WM_NCCALCSIZE, TRUE, (LPARAM)&params );
3165 *new_client_rect = params.rgrc[0];
3167 TRACE( "hwnd %p old win %s old client %s new win %s new client %s\n", winpos->hwnd,
3168 wine_dbgstr_rect(old_window_rect), wine_dbgstr_rect(old_client_rect),
3169 wine_dbgstr_rect(new_window_rect), wine_dbgstr_rect(new_client_rect) );
3171 if (new_client_rect->left != old_client_rect->left - parent_x ||
3172 new_client_rect->top != old_client_rect->top - parent_y)
3173 winpos->flags &= ~SWP_NOCLIENTMOVE;
3175 if ((new_client_rect->right - new_client_rect->left !=
3176 old_client_rect->right - old_client_rect->left))
3177 winpos->flags &= ~SWP_NOCLIENTSIZE;
3178 else
3179 wvr_flags &= ~WVR_HREDRAW;
3181 if (new_client_rect->bottom - new_client_rect->top !=
3182 old_client_rect->bottom - old_client_rect->top)
3183 winpos->flags &= ~SWP_NOCLIENTSIZE;
3184 else
3185 wvr_flags &= ~WVR_VREDRAW;
3187 valid_rects[0] = params.rgrc[1];
3188 valid_rects[1] = params.rgrc[2];
3190 else
3192 if (!(winpos->flags & SWP_NOMOVE) &&
3193 (new_client_rect->left != old_client_rect->left - parent_x ||
3194 new_client_rect->top != old_client_rect->top - parent_y))
3195 winpos->flags &= ~SWP_NOCLIENTMOVE;
3198 if (winpos->flags & (SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_SHOWWINDOW | SWP_HIDEWINDOW))
3200 SetRectEmpty( &valid_rects[0] );
3201 SetRectEmpty( &valid_rects[1] );
3203 else get_valid_rects( old_client_rect, new_client_rect, wvr_flags, valid_rects );
3205 return wvr_flags;
3208 /* fix redundant flags and values in the WINDOWPOS structure */
3209 static BOOL fixup_swp_flags( WINDOWPOS *winpos, const RECT *old_window_rect, int parent_x, int parent_y )
3211 HWND parent;
3212 WND *win = get_win_ptr( winpos->hwnd );
3213 BOOL ret = TRUE;
3215 if (!win || win == WND_OTHER_PROCESS)
3217 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
3218 return FALSE;
3220 winpos->hwnd = win->obj.handle; /* make it a full handle */
3222 /* Finally make sure that all coordinates are valid */
3223 if (winpos->x < -32768) winpos->x = -32768;
3224 else if (winpos->x > 32767) winpos->x = 32767;
3225 if (winpos->y < -32768) winpos->y = -32768;
3226 else if (winpos->y > 32767) winpos->y = 32767;
3228 if (winpos->cx < 0) winpos->cx = 0;
3229 else if (winpos->cx > 32767) winpos->cx = 32767;
3230 if (winpos->cy < 0) winpos->cy = 0;
3231 else if (winpos->cy > 32767) winpos->cy = 32767;
3233 parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3234 if (!is_window_visible( parent )) winpos->flags |= SWP_NOREDRAW;
3236 if (win->dwStyle & WS_VISIBLE) winpos->flags &= ~SWP_SHOWWINDOW;
3237 else
3239 winpos->flags &= ~SWP_HIDEWINDOW;
3240 if (!(winpos->flags & SWP_SHOWWINDOW)) winpos->flags |= SWP_NOREDRAW;
3243 if ((old_window_rect->right - old_window_rect->left == winpos->cx) &&
3244 (old_window_rect->bottom - old_window_rect->top == winpos->cy))
3245 winpos->flags |= SWP_NOSIZE; /* Already the right size */
3247 if ((old_window_rect->left - parent_x == winpos->x) && (old_window_rect->top - parent_y == winpos->y))
3248 winpos->flags |= SWP_NOMOVE; /* Already the right position */
3250 if ((win->dwStyle & (WS_POPUP | WS_CHILD)) != WS_CHILD)
3252 if (!(winpos->flags & (SWP_NOACTIVATE|SWP_HIDEWINDOW)) && /* Bring to the top when activating */
3253 (winpos->flags & SWP_NOZORDER ||
3254 (winpos->hwndInsertAfter != HWND_TOPMOST && winpos->hwndInsertAfter != HWND_NOTOPMOST)))
3256 winpos->flags &= ~SWP_NOZORDER;
3257 winpos->hwndInsertAfter = HWND_TOP;
3261 /* Check hwndInsertAfter */
3262 if (winpos->flags & SWP_NOZORDER) goto done;
3264 if (winpos->hwndInsertAfter == HWND_TOP)
3266 if (get_window_relative( winpos->hwnd, GW_HWNDFIRST ) == winpos->hwnd)
3267 winpos->flags |= SWP_NOZORDER;
3269 else if (winpos->hwndInsertAfter == HWND_BOTTOM)
3271 if (!(win->dwExStyle & WS_EX_TOPMOST) &&
3272 get_window_relative( winpos->hwnd, GW_HWNDLAST ) == winpos->hwnd)
3273 winpos->flags |= SWP_NOZORDER;
3275 else if (winpos->hwndInsertAfter == HWND_TOPMOST)
3277 if ((win->dwExStyle & WS_EX_TOPMOST) &&
3278 get_window_relative( winpos->hwnd, GW_HWNDFIRST ) == winpos->hwnd)
3279 winpos->flags |= SWP_NOZORDER;
3281 else if (winpos->hwndInsertAfter == HWND_NOTOPMOST)
3283 if (!(win->dwExStyle & WS_EX_TOPMOST))
3284 winpos->flags |= SWP_NOZORDER;
3286 else
3288 if ((winpos->hwnd == winpos->hwndInsertAfter) ||
3289 (winpos->hwnd == get_window_relative( winpos->hwndInsertAfter, GW_HWNDNEXT )))
3290 winpos->flags |= SWP_NOZORDER;
3292 done:
3293 release_win_ptr( win );
3294 return ret;
3297 /***********************************************************************
3298 * swp_owner_popups
3300 * fix Z order taking into account owned popups -
3301 * basically we need to maintain them above the window that owns them
3303 * FIXME: hide/show owned popups when owner visibility changes.
3305 static HWND swp_owner_popups( HWND hwnd, HWND after )
3307 HWND owner, *list = NULL;
3308 unsigned int i;
3310 TRACE( "(%p) after = %p\n", hwnd, after );
3312 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) return after;
3314 if ((owner = get_window_relative( hwnd, GW_OWNER )))
3316 /* make sure this popup stays above the owner */
3318 if (after != HWND_TOPMOST)
3320 if (!(list = list_window_children( 0, get_desktop_window(), NULL, 0 ))) return after;
3322 for (i = 0; list[i]; i++)
3324 BOOL topmost = (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TOPMOST) != 0;
3326 if (list[i] == owner)
3328 if (i > 0) after = list[i-1];
3329 else after = topmost ? HWND_TOPMOST : HWND_TOP;
3330 break;
3333 if (after == HWND_TOP || after == HWND_NOTOPMOST)
3335 if (!topmost) break;
3337 else if (list[i] == after) break;
3342 if (after == HWND_BOTTOM) goto done;
3343 if (!list && !(list = list_window_children( 0, get_desktop_window(), NULL, 0 ))) goto done;
3345 i = 0;
3346 if (after == HWND_TOP || after == HWND_NOTOPMOST)
3348 if (after == HWND_NOTOPMOST ||
3349 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST))
3351 /* skip all the topmost windows */
3352 while (list[i] && (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TOPMOST)) i++;
3355 else if (after != HWND_TOPMOST)
3357 /* skip windows that are already placed correctly */
3358 for (i = 0; list[i]; i++)
3360 if (list[i] == after) break;
3361 if (list[i] == hwnd) goto done; /* nothing to do if window is moving backwards in z-order */
3365 for ( ; list[i]; i++)
3367 if (list[i] == hwnd) break;
3368 if (get_window_relative( list[i], GW_OWNER ) != hwnd) continue;
3369 TRACE( "moving %p owned by %p after %p\n", list[i], hwnd, after );
3370 NtUserSetWindowPos( list[i], after, 0, 0, 0, 0,
3371 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_DEFERERASE );
3372 after = list[i];
3375 done:
3376 free( list );
3377 return after;
3380 /* NtUserSetWindowPos implementation */
3381 BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y )
3383 RECT old_window_rect, old_client_rect, new_window_rect, new_client_rect, valid_rects[2];
3384 UINT orig_flags;
3385 BOOL ret = FALSE;
3386 DPI_AWARENESS_CONTEXT context;
3388 orig_flags = winpos->flags;
3390 /* First, check z-order arguments. */
3391 if (!(winpos->flags & SWP_NOZORDER))
3393 /* fix sign extension */
3394 if (winpos->hwndInsertAfter == (HWND)0xffff) winpos->hwndInsertAfter = HWND_TOPMOST;
3395 else if (winpos->hwndInsertAfter == (HWND)0xfffe) winpos->hwndInsertAfter = HWND_NOTOPMOST;
3397 if (!(winpos->hwndInsertAfter == HWND_TOP ||
3398 winpos->hwndInsertAfter == HWND_BOTTOM ||
3399 winpos->hwndInsertAfter == HWND_TOPMOST ||
3400 winpos->hwndInsertAfter == HWND_NOTOPMOST))
3402 HWND parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3403 HWND insertafter_parent = NtUserGetAncestor( winpos->hwndInsertAfter, GA_PARENT );
3405 /* hwndInsertAfter must be a sibling of the window */
3406 if (!insertafter_parent) return FALSE;
3407 if (insertafter_parent != parent) return TRUE;
3411 /* Make sure that coordinates are valid for WM_WINDOWPOSCHANGING */
3412 if (!(winpos->flags & SWP_NOMOVE))
3414 if (winpos->x < -32768) winpos->x = -32768;
3415 else if (winpos->x > 32767) winpos->x = 32767;
3416 if (winpos->y < -32768) winpos->y = -32768;
3417 else if (winpos->y > 32767) winpos->y = 32767;
3419 if (!(winpos->flags & SWP_NOSIZE))
3421 if (winpos->cx < 0) winpos->cx = 0;
3422 else if (winpos->cx > 32767) winpos->cx = 32767;
3423 if (winpos->cy < 0) winpos->cy = 0;
3424 else if (winpos->cy > 32767) winpos->cy = 32767;
3427 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( winpos->hwnd ));
3429 if (!calc_winpos( winpos, &old_window_rect, &old_client_rect,
3430 &new_window_rect, &new_client_rect )) goto done;
3432 /* Fix redundant flags */
3433 if (!fixup_swp_flags( winpos, &old_window_rect, parent_x, parent_y )) goto done;
3435 if((winpos->flags & (SWP_NOZORDER | SWP_HIDEWINDOW | SWP_SHOWWINDOW)) != SWP_NOZORDER)
3437 if (NtUserGetAncestor( winpos->hwnd, GA_PARENT ) == get_desktop_window())
3438 winpos->hwndInsertAfter = swp_owner_popups( winpos->hwnd, winpos->hwndInsertAfter );
3441 /* Common operations */
3443 calc_ncsize( winpos, &old_window_rect, &old_client_rect,
3444 &new_window_rect, &new_client_rect, valid_rects, parent_x, parent_y );
3446 if (!apply_window_pos( winpos->hwnd, winpos->hwndInsertAfter, winpos->flags,
3447 &new_window_rect, &new_client_rect, valid_rects ))
3448 goto done;
3450 if (winpos->flags & SWP_HIDEWINDOW)
3452 NtUserNotifyWinEvent( EVENT_OBJECT_HIDE, winpos->hwnd, 0, 0 );
3454 NtUserHideCaret( winpos->hwnd );
3456 else if (winpos->flags & SWP_SHOWWINDOW)
3458 NtUserNotifyWinEvent( EVENT_OBJECT_SHOW, winpos->hwnd, 0, 0 );
3460 NtUserShowCaret( winpos->hwnd );
3463 if (!(winpos->flags & (SWP_NOACTIVATE|SWP_HIDEWINDOW)))
3465 /* child windows get WM_CHILDACTIVATE message */
3466 if ((get_window_long( winpos->hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD)
3467 send_message( winpos->hwnd, WM_CHILDACTIVATE, 0, 0 );
3468 else
3469 set_foreground_window( winpos->hwnd, FALSE );
3472 if(!(orig_flags & SWP_DEFERERASE))
3474 /* erase parent when hiding or resizing child */
3475 if ((orig_flags & SWP_HIDEWINDOW) ||
3476 (!(orig_flags & SWP_SHOWWINDOW) &&
3477 (winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOGEOMETRYCHANGE))
3479 HWND parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3480 if (!parent || parent == get_desktop_window()) parent = winpos->hwnd;
3481 erase_now( parent, 0 );
3484 /* Give newly shown windows a chance to redraw */
3485 if(((winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOPOSCHANGE)
3486 && !(orig_flags & SWP_AGG_NOCLIENTCHANGE) && (orig_flags & SWP_SHOWWINDOW))
3488 erase_now(winpos->hwnd, 0);
3492 /* And last, send the WM_WINDOWPOSCHANGED message */
3494 TRACE( "\tstatus flags = %04x\n", winpos->flags & SWP_AGG_STATUSFLAGS );
3496 if (((winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOPOSCHANGE)
3497 && !((orig_flags & SWP_AGG_NOCLIENTCHANGE) && (orig_flags & SWP_SHOWWINDOW)))
3499 /* WM_WINDOWPOSCHANGED is sent even if SWP_NOSENDCHANGING is set
3500 and always contains final window position.
3502 winpos->x = new_window_rect.left;
3503 winpos->y = new_window_rect.top;
3504 winpos->cx = new_window_rect.right - new_window_rect.left;
3505 winpos->cy = new_window_rect.bottom - new_window_rect.top;
3506 send_message( winpos->hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)winpos );
3508 ret = TRUE;
3509 done:
3510 SetThreadDpiAwarenessContext( context );
3511 return ret;
3514 /*******************************************************************
3515 * NtUserSetWindowPos (win32u.@)
3517 BOOL WINAPI NtUserSetWindowPos( HWND hwnd, HWND after, INT x, INT y, INT cx, INT cy, UINT flags )
3519 WINDOWPOS winpos;
3521 TRACE( "hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n", hwnd, after, x, y, cx, cy, flags );
3522 if(TRACE_ON(win)) dump_winpos_flags(flags);
3524 if (is_broadcast( hwnd ))
3526 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
3527 return FALSE;
3530 winpos.hwnd = get_full_window_handle( hwnd );
3531 winpos.hwndInsertAfter = get_full_window_handle( after );
3532 winpos.x = x;
3533 winpos.y = y;
3534 winpos.cx = cx;
3535 winpos.cy = cy;
3536 winpos.flags = flags;
3538 map_dpi_winpos( &winpos );
3540 if (is_current_thread_window( hwnd ))
3541 return set_window_pos( &winpos, 0, 0 );
3543 if (flags & SWP_ASYNCWINDOWPOS)
3544 return NtUserMessageCall( winpos.hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)&winpos,
3545 0, NtUserSendNotifyMessage, FALSE );
3546 else
3547 return send_message( winpos.hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)&winpos );
3550 typedef struct
3552 struct user_object obj;
3553 INT count;
3554 INT suggested_count;
3555 HWND parent;
3556 WINDOWPOS *winpos;
3557 } DWP;
3559 /* see BeginDeferWindowPos */
3560 HDWP begin_defer_window_pos( INT count )
3562 HDWP handle = 0;
3563 DWP *dwp;
3565 TRACE( "%d\n", count );
3567 if (count < 0)
3569 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
3570 return 0;
3572 /* Windows allows zero count, in which case it allocates context for 8 moves */
3573 if (count == 0) count = 8;
3575 if (!(dwp = malloc( sizeof(DWP) ))) return 0;
3577 dwp->count = 0;
3578 dwp->parent = 0;
3579 dwp->suggested_count = count;
3581 if (!(dwp->winpos = malloc( count * sizeof(WINDOWPOS) )) ||
3582 !(handle = alloc_user_handle( &dwp->obj, NTUSER_OBJ_WINPOS )))
3584 free( dwp->winpos );
3585 free( dwp );
3588 TRACE( "returning %p\n", handle );
3589 return handle;
3592 /***********************************************************************
3593 * NtUserDeferWindowPosAndBand (win32u.@)
3595 HDWP WINAPI NtUserDeferWindowPosAndBand( HDWP hdwp, HWND hwnd, HWND after,
3596 INT x, INT y, INT cx, INT cy,
3597 UINT flags, UINT unk1, UINT unk2 )
3599 HDWP retvalue = hdwp;
3600 WINDOWPOS winpos;
3601 DWP *dwp;
3602 int i;
3604 TRACE( "hdwp %p, hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n",
3605 hdwp, hwnd, after, x, y, cx, cy, flags );
3607 winpos.hwnd = get_full_window_handle( hwnd );
3608 if (is_desktop_window( winpos.hwnd ) || !is_window( winpos.hwnd ))
3610 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
3611 return 0;
3614 winpos.hwndInsertAfter = get_full_window_handle( after );
3615 winpos.flags = flags;
3616 winpos.x = x;
3617 winpos.y = y;
3618 winpos.cx = cx;
3619 winpos.cy = cy;
3620 map_dpi_winpos( &winpos );
3622 if (!(dwp = get_user_handle_ptr( hdwp, NTUSER_OBJ_WINPOS ))) return 0;
3623 if (dwp == OBJ_OTHER_PROCESS)
3625 FIXME( "other process handle %p\n", hdwp );
3626 return 0;
3629 for (i = 0; i < dwp->count; i++)
3631 if (dwp->winpos[i].hwnd == winpos.hwnd)
3633 /* Merge with the other changes */
3634 if (!(flags & SWP_NOZORDER))
3636 dwp->winpos[i].hwndInsertAfter = winpos.hwndInsertAfter;
3638 if (!(flags & SWP_NOMOVE))
3640 dwp->winpos[i].x = winpos.x;
3641 dwp->winpos[i].y = winpos.y;
3643 if (!(flags & SWP_NOSIZE))
3645 dwp->winpos[i].cx = winpos.cx;
3646 dwp->winpos[i].cy = winpos.cy;
3648 dwp->winpos[i].flags &= flags | ~(SWP_NOSIZE | SWP_NOMOVE |
3649 SWP_NOZORDER | SWP_NOREDRAW |
3650 SWP_NOACTIVATE | SWP_NOCOPYBITS|
3651 SWP_NOOWNERZORDER);
3652 dwp->winpos[i].flags |= flags & (SWP_SHOWWINDOW | SWP_HIDEWINDOW |
3653 SWP_FRAMECHANGED);
3654 goto done;
3657 if (dwp->count >= dwp->suggested_count)
3659 WINDOWPOS *newpos = realloc( dwp->winpos, dwp->suggested_count * 2 * sizeof(WINDOWPOS) );
3660 if (!newpos)
3662 retvalue = 0;
3663 goto done;
3665 dwp->suggested_count *= 2;
3666 dwp->winpos = newpos;
3668 dwp->winpos[dwp->count++] = winpos;
3669 done:
3670 release_user_handle_ptr( dwp );
3671 return retvalue;
3674 /***********************************************************************
3675 * NtUserEndDeferWindowPosEx (win32u.@)
3677 BOOL WINAPI NtUserEndDeferWindowPosEx( HDWP hdwp, BOOL async )
3679 WINDOWPOS *winpos;
3680 DWP *dwp;
3681 int i;
3683 TRACE( "%p\n", hdwp );
3685 if (async) FIXME( "async not supported\n" );
3687 if (!(dwp = free_user_handle( hdwp, NTUSER_OBJ_WINPOS ))) return FALSE;
3688 if (dwp == OBJ_OTHER_PROCESS)
3690 FIXME( "other process handle %p\n", hdwp );
3691 return FALSE;
3694 for (i = 0, winpos = dwp->winpos; i < dwp->count; i++, winpos++)
3696 TRACE( "hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n",
3697 winpos->hwnd, winpos->hwndInsertAfter, winpos->x, winpos->y,
3698 winpos->cx, winpos->cy, winpos->flags );
3700 if (is_current_thread_window( winpos->hwnd ))
3701 set_window_pos( winpos, 0, 0 );
3702 else
3703 send_message( winpos->hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)winpos );
3705 free( dwp->winpos );
3706 free( dwp );
3707 return TRUE;
3710 /***********************************************************************
3711 * NtUserSetInternalWindowPos (win32u.@)
3713 void WINAPI NtUserSetInternalWindowPos( HWND hwnd, UINT cmd, RECT *rect, POINT *pt )
3715 WINDOWPLACEMENT wndpl;
3716 UINT flags;
3718 wndpl.length = sizeof(wndpl);
3719 wndpl.showCmd = cmd;
3720 wndpl.flags = flags = 0;
3722 if (pt)
3724 flags |= PLACE_MIN;
3725 wndpl.flags |= WPF_SETMINPOSITION;
3726 wndpl.ptMinPosition = *pt;
3728 if( rect )
3730 flags |= PLACE_RECT;
3731 wndpl.rcNormalPosition = *rect;
3733 set_window_placement( hwnd, &wndpl, flags );
3736 /***********************************************************************
3737 * win_set_flags
3739 * Set the flags of a window and return the previous value.
3741 UINT win_set_flags( HWND hwnd, UINT set_mask, UINT clear_mask )
3743 WND *win = get_win_ptr( hwnd );
3744 UINT ret;
3746 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return 0;
3747 ret = win->flags;
3748 win->flags = (ret & ~clear_mask) | set_mask;
3749 release_win_ptr( win );
3750 return ret;
3753 /*******************************************************************
3754 * can_activate_window
3756 * Check if we can activate the specified window.
3758 static BOOL can_activate_window( HWND hwnd )
3760 LONG style;
3762 if (!hwnd) return FALSE;
3763 style = get_window_long( hwnd, GWL_STYLE );
3764 if (!(style & WS_VISIBLE)) return FALSE;
3765 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
3766 return !(style & WS_DISABLED);
3769 /*******************************************************************
3770 * activate_other_window
3772 * Activates window other than hwnd.
3774 static void activate_other_window( HWND hwnd )
3776 HWND hwnd_to, fg;
3778 if ((get_window_long( hwnd, GWL_STYLE ) & WS_POPUP) &&
3779 (hwnd_to = get_window_relative( hwnd, GW_OWNER )))
3781 hwnd_to = NtUserGetAncestor( hwnd_to, GA_ROOT );
3782 if (can_activate_window( hwnd_to )) goto done;
3785 hwnd_to = hwnd;
3786 for (;;)
3788 if (!(hwnd_to = get_window_relative( hwnd_to, GW_HWNDNEXT ))) break;
3789 if (can_activate_window( hwnd_to )) goto done;
3792 hwnd_to = get_window_relative( get_desktop_window(), GW_CHILD );
3793 for (;;)
3795 if (hwnd_to == hwnd)
3797 hwnd_to = 0;
3798 break;
3800 if (can_activate_window( hwnd_to )) goto done;
3801 if (!(hwnd_to = get_window_relative( hwnd_to, GW_HWNDNEXT ))) break;
3804 done:
3805 fg = NtUserGetForegroundWindow();
3806 TRACE( "win = %p fg = %p\n", hwnd_to, fg );
3807 if (!fg || hwnd == fg)
3809 if (set_foreground_window( hwnd_to, FALSE )) return;
3811 if (NtUserSetActiveWindow( hwnd_to )) NtUserSetActiveWindow( 0 );
3814 /*******************************************************************
3815 * send_parent_notify
3817 static void send_parent_notify( HWND hwnd, UINT msg )
3819 if ((get_window_long( hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD &&
3820 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY))
3822 HWND parent = get_parent( hwnd );
3823 if (parent && parent != get_desktop_window())
3824 send_message( parent, WM_PARENTNOTIFY,
3825 MAKEWPARAM( msg, get_window_long( hwnd, GWLP_ID )), (LPARAM)hwnd );
3829 /*******************************************************************
3830 * get_min_max_info
3832 * Get the minimized and maximized information for a window.
3834 MINMAXINFO get_min_max_info( HWND hwnd )
3836 LONG style = get_window_long( hwnd, GWL_STYLE );
3837 LONG exstyle = get_window_long( hwnd, GWL_EXSTYLE );
3838 DPI_AWARENESS_CONTEXT context;
3839 RECT rc_work, rc_primary;
3840 LONG adjusted_style;
3841 MINMAXINFO minmax;
3842 INT xinc, yinc;
3843 RECT rc;
3844 WND *win;
3846 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
3848 /* Compute default values */
3850 get_window_rect( hwnd, &rc, get_thread_dpi() );
3851 minmax.ptReserved.x = rc.left;
3852 minmax.ptReserved.y = rc.top;
3854 if ((style & WS_CAPTION) == WS_CAPTION)
3855 adjusted_style = style & ~WS_BORDER; /* WS_CAPTION = WS_DLGFRAME | WS_BORDER */
3856 else
3857 adjusted_style = style;
3859 get_client_rect( NtUserGetAncestor( hwnd, GA_PARENT ), &rc );
3860 AdjustWindowRectEx( &rc, adjusted_style, (style & WS_POPUP) && get_menu( hwnd ), exstyle );
3862 xinc = -rc.left;
3863 yinc = -rc.top;
3865 minmax.ptMaxSize.x = rc.right - rc.left;
3866 minmax.ptMaxSize.y = rc.bottom - rc.top;
3867 if (style & (WS_DLGFRAME | WS_BORDER))
3869 minmax.ptMinTrackSize.x = get_system_metrics( SM_CXMINTRACK );
3870 minmax.ptMinTrackSize.y = get_system_metrics( SM_CYMINTRACK );
3872 else
3874 minmax.ptMinTrackSize.x = 2 * xinc;
3875 minmax.ptMinTrackSize.y = 2 * yinc;
3877 minmax.ptMaxTrackSize.x = get_system_metrics( SM_CXMAXTRACK );
3878 minmax.ptMaxTrackSize.y = get_system_metrics( SM_CYMAXTRACK );
3879 minmax.ptMaxPosition.x = -xinc;
3880 minmax.ptMaxPosition.y = -yinc;
3882 if ((win = get_win_ptr( hwnd )) && win != WND_DESKTOP && win != WND_OTHER_PROCESS)
3884 if (!empty_point( win->max_pos )) minmax.ptMaxPosition = win->max_pos;
3885 release_win_ptr( win );
3888 send_message( hwnd, WM_GETMINMAXINFO, 0, (LPARAM)&minmax );
3890 /* if the app didn't change the values, adapt them for the current monitor */
3892 if (get_work_rect( hwnd, &rc_work ))
3894 rc_primary = get_primary_monitor_rect( get_thread_dpi() );
3895 if (minmax.ptMaxSize.x == (rc_primary.right - rc_primary.left) + 2 * xinc &&
3896 minmax.ptMaxSize.y == (rc_primary.bottom - rc_primary.top) + 2 * yinc)
3898 minmax.ptMaxSize.x = (rc_work.right - rc_work.left) + 2 * xinc;
3899 minmax.ptMaxSize.y = (rc_work.bottom - rc_work.top) + 2 * yinc;
3901 if (minmax.ptMaxPosition.x == -xinc && minmax.ptMaxPosition.y == -yinc)
3903 minmax.ptMaxPosition.x = rc_work.left - xinc;
3904 minmax.ptMaxPosition.y = rc_work.top - yinc;
3908 TRACE( "%d %d / %d %d / %d %d / %d %d\n",
3909 (int)minmax.ptMaxSize.x, (int)minmax.ptMaxSize.y,
3910 (int)minmax.ptMaxPosition.x, (int)minmax.ptMaxPosition.y,
3911 (int)minmax.ptMaxTrackSize.x, (int)minmax.ptMaxTrackSize.y,
3912 (int)minmax.ptMinTrackSize.x, (int)minmax.ptMinTrackSize.y );
3914 minmax.ptMaxTrackSize.x = max( minmax.ptMaxTrackSize.x, minmax.ptMinTrackSize.x );
3915 minmax.ptMaxTrackSize.y = max( minmax.ptMaxTrackSize.y, minmax.ptMinTrackSize.y );
3917 SetThreadDpiAwarenessContext( context );
3918 return minmax;
3921 static POINT get_first_minimized_child_pos( const RECT *parent, const MINIMIZEDMETRICS *mm,
3922 int width, int height )
3924 POINT ret;
3926 if (mm->iArrange & ARW_STARTRIGHT)
3927 ret.x = parent->right - mm->iHorzGap - width;
3928 else
3929 ret.x = parent->left + mm->iHorzGap;
3930 if (mm->iArrange & ARW_STARTTOP)
3931 ret.y = parent->top + mm->iVertGap;
3932 else
3933 ret.y = parent->bottom - mm->iVertGap - height;
3935 return ret;
3938 static void get_next_minimized_child_pos( const RECT *parent, const MINIMIZEDMETRICS *mm,
3939 int width, int height, POINT *pos )
3941 BOOL next;
3943 if (mm->iArrange & ARW_UP) /* == ARW_DOWN */
3945 if (mm->iArrange & ARW_STARTTOP)
3947 pos->y += height + mm->iVertGap;
3948 if ((next = pos->y + height > parent->bottom))
3949 pos->y = parent->top + mm->iVertGap;
3951 else
3953 pos->y -= height + mm->iVertGap;
3954 if ((next = pos->y < parent->top))
3955 pos->y = parent->bottom - mm->iVertGap - height;
3958 if (next)
3960 if (mm->iArrange & ARW_STARTRIGHT)
3961 pos->x -= width + mm->iHorzGap;
3962 else
3963 pos->x += width + mm->iHorzGap;
3966 else
3968 if (mm->iArrange & ARW_STARTRIGHT)
3970 pos->x -= width + mm->iHorzGap;
3971 if ((next = pos->x < parent->left))
3972 pos->x = parent->right - mm->iHorzGap - width;
3974 else
3976 pos->x += width + mm->iHorzGap;
3977 if ((next = pos->x + width > parent->right))
3978 pos->x = parent->left + mm->iHorzGap;
3981 if (next)
3983 if (mm->iArrange & ARW_STARTTOP)
3984 pos->y += height + mm->iVertGap;
3985 else
3986 pos->y -= height + mm->iVertGap;
3991 static POINT get_minimized_pos( HWND hwnd, POINT pt )
3993 RECT rect, parent_rect;
3994 HWND parent, child;
3995 HRGN hrgn, tmp;
3996 MINIMIZEDMETRICS metrics;
3997 int width, height;
3999 parent = NtUserGetAncestor( hwnd, GA_PARENT );
4000 if (parent == get_desktop_window())
4002 MONITORINFO mon_info;
4003 HMONITOR monitor = monitor_from_window( hwnd, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
4005 mon_info.cbSize = sizeof( mon_info );
4006 get_monitor_info( monitor, &mon_info );
4007 parent_rect = mon_info.rcWork;
4009 else get_client_rect( parent, &parent_rect );
4011 if (pt.x >= parent_rect.left && (pt.x + get_system_metrics( SM_CXMINIMIZED ) < parent_rect.right) &&
4012 pt.y >= parent_rect.top && (pt.y + get_system_metrics( SM_CYMINIMIZED ) < parent_rect.bottom))
4013 return pt; /* The icon already has a suitable position */
4015 width = get_system_metrics( SM_CXMINIMIZED );
4016 height = get_system_metrics( SM_CYMINIMIZED );
4018 metrics.cbSize = sizeof(metrics);
4019 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(metrics), &metrics, 0 );
4021 /* Check if another icon already occupies this spot */
4022 /* FIXME: this is completely inefficient */
4024 hrgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
4025 tmp = NtGdiCreateRectRgn( 0, 0, 0, 0 );
4026 for (child = get_window_relative( parent, GW_CHILD );
4027 child;
4028 child = get_window_relative( child, GW_HWNDNEXT ))
4030 if (child == hwnd) continue;
4031 if ((get_window_long( child, GWL_STYLE ) & (WS_VISIBLE|WS_MINIMIZE)) != (WS_VISIBLE|WS_MINIMIZE))
4032 continue;
4033 if (get_window_rects( child, COORDS_PARENT, &rect, NULL, get_thread_dpi() ))
4035 NtGdiSetRectRgn( tmp, rect.left, rect.top, rect.right, rect.bottom );
4036 NtGdiCombineRgn( hrgn, hrgn, tmp, RGN_OR );
4039 NtGdiDeleteObjectApp( tmp );
4041 pt = get_first_minimized_child_pos( &parent_rect, &metrics, width, height );
4042 for (;;)
4044 SetRect( &rect, pt.x, pt.y, pt.x + width, pt.y + height );
4045 if (!NtGdiRectInRegion( hrgn, &rect ))
4046 break;
4048 get_next_minimized_child_pos( &parent_rect, &metrics, width, height, &pt );
4051 NtGdiDeleteObjectApp( hrgn );
4052 return pt;
4055 /***********************************************************************
4056 * window_min_maximize
4058 static UINT window_min_maximize( HWND hwnd, UINT cmd, RECT *rect )
4060 UINT swp_flags = 0;
4061 LONG old_style;
4062 MINMAXINFO minmax;
4063 WINDOWPLACEMENT wpl;
4065 TRACE( "%p %u\n", hwnd, cmd );
4067 wpl.length = sizeof(wpl);
4068 NtUserGetWindowPlacement( hwnd, &wpl );
4070 if (call_hooks( WH_CBT, HCBT_MINMAX, (WPARAM)hwnd, cmd, 0 ))
4071 return SWP_NOSIZE | SWP_NOMOVE;
4073 if (is_iconic( hwnd ))
4075 switch (cmd)
4077 case SW_SHOWMINNOACTIVE:
4078 case SW_SHOWMINIMIZED:
4079 case SW_FORCEMINIMIZE:
4080 case SW_MINIMIZE:
4081 wpl.ptMinPosition = get_minimized_pos( hwnd, wpl.ptMinPosition );
4083 SetRect( rect, wpl.ptMinPosition.x, wpl.ptMinPosition.y,
4084 wpl.ptMinPosition.x + get_system_metrics( SM_CXMINIMIZED ),
4085 wpl.ptMinPosition.y + get_system_metrics( SM_CYMINIMIZED ));
4086 return SWP_NOSIZE | SWP_NOMOVE;
4088 if (!send_message( hwnd, WM_QUERYOPEN, 0, 0 )) return SWP_NOSIZE | SWP_NOMOVE;
4089 swp_flags |= SWP_NOCOPYBITS;
4092 switch( cmd )
4094 case SW_SHOWMINNOACTIVE:
4095 case SW_SHOWMINIMIZED:
4096 case SW_FORCEMINIMIZE:
4097 case SW_MINIMIZE:
4098 if (is_zoomed( hwnd )) win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
4099 else win_set_flags( hwnd, 0, WIN_RESTORE_MAX );
4101 if (get_focus() == hwnd)
4103 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD)
4104 NtUserSetFocus( NtUserGetAncestor( hwnd, GA_PARENT ));
4105 else
4106 NtUserSetFocus( 0 );
4109 old_style = set_window_style( hwnd, WS_MINIMIZE, WS_MAXIMIZE );
4111 wpl.ptMinPosition = get_minimized_pos( hwnd, wpl.ptMinPosition );
4113 if (!(old_style & WS_MINIMIZE)) swp_flags |= SWP_STATECHANGED;
4114 SetRect( rect, wpl.ptMinPosition.x, wpl.ptMinPosition.y,
4115 wpl.ptMinPosition.x + get_system_metrics(SM_CXMINIMIZED),
4116 wpl.ptMinPosition.y + get_system_metrics(SM_CYMINIMIZED) );
4117 swp_flags |= SWP_NOCOPYBITS;
4118 break;
4120 case SW_MAXIMIZE:
4121 old_style = get_window_long( hwnd, GWL_STYLE );
4122 if ((old_style & WS_MAXIMIZE) && (old_style & WS_VISIBLE)) return SWP_NOSIZE | SWP_NOMOVE;
4124 minmax = get_min_max_info( hwnd );
4126 old_style = set_window_style( hwnd, WS_MAXIMIZE, WS_MINIMIZE );
4127 if (old_style & WS_MINIMIZE)
4128 win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
4130 if (!(old_style & WS_MAXIMIZE)) swp_flags |= SWP_STATECHANGED;
4131 SetRect( rect, minmax.ptMaxPosition.x, minmax.ptMaxPosition.y,
4132 minmax.ptMaxPosition.x + minmax.ptMaxSize.x,
4133 minmax.ptMaxPosition.y + minmax.ptMaxSize.y );
4134 break;
4136 case SW_SHOWNOACTIVATE:
4137 win_set_flags( hwnd, 0, WIN_RESTORE_MAX );
4138 /* fall through */
4139 case SW_SHOWNORMAL:
4140 case SW_RESTORE:
4141 case SW_SHOWDEFAULT: /* FIXME: should have its own handler */
4142 old_style = set_window_style( hwnd, 0, WS_MINIMIZE | WS_MAXIMIZE );
4143 if (old_style & WS_MINIMIZE)
4145 if (win_get_flags( hwnd ) & WIN_RESTORE_MAX)
4147 /* Restore to maximized position */
4148 minmax = get_min_max_info( hwnd );
4149 set_window_style( hwnd, WS_MAXIMIZE, 0 );
4150 swp_flags |= SWP_STATECHANGED;
4151 SetRect( rect, minmax.ptMaxPosition.x, minmax.ptMaxPosition.y,
4152 minmax.ptMaxPosition.x + minmax.ptMaxSize.x,
4153 minmax.ptMaxPosition.y + minmax.ptMaxSize.y );
4154 break;
4157 else if (!(old_style & WS_MAXIMIZE)) break;
4159 swp_flags |= SWP_STATECHANGED;
4161 /* Restore to normal position */
4163 *rect = wpl.rcNormalPosition;
4164 break;
4167 return swp_flags;
4170 /* see ArrangeIconicWindows */
4171 static UINT arrange_iconic_windows( HWND parent )
4173 int width, height, count = 0;
4174 MINIMIZEDMETRICS metrics;
4175 RECT parent_rect;
4176 HWND child;
4177 POINT pt;
4179 metrics.cbSize = sizeof(metrics);
4180 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(metrics), &metrics, 0 );
4181 width = get_system_metrics( SM_CXMINIMIZED );
4182 height = get_system_metrics( SM_CYMINIMIZED );
4184 if (parent == get_desktop_window())
4186 MONITORINFO mon_info;
4187 HMONITOR monitor = monitor_from_window( 0, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
4189 mon_info.cbSize = sizeof( mon_info );
4190 get_monitor_info( monitor, &mon_info );
4191 parent_rect = mon_info.rcWork;
4193 else get_client_rect( parent, &parent_rect );
4195 pt = get_first_minimized_child_pos( &parent_rect, &metrics, width, height );
4197 child = get_window_relative( parent, GW_CHILD );
4198 while (child)
4200 if (is_iconic( child ))
4202 NtUserSetWindowPos( child, 0, pt.x, pt.y, 0, 0,
4203 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
4204 get_next_minimized_child_pos( &parent_rect, &metrics, width, height, &pt );
4205 count++;
4207 child = get_window_relative( child, GW_HWNDNEXT );
4209 return count;
4212 /*******************************************************************
4213 * update_window_state
4215 * Trigger an update of the window's driver state and surface.
4217 void update_window_state( HWND hwnd )
4219 DPI_AWARENESS_CONTEXT context;
4220 RECT window_rect, client_rect, valid_rects[2];
4222 if (!is_current_thread_window( hwnd ))
4224 NtUserPostMessage( hwnd, WM_WINE_UPDATEWINDOWSTATE, 0, 0 );
4225 return;
4228 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
4229 get_window_rects( hwnd, COORDS_PARENT, &window_rect, &client_rect, get_thread_dpi() );
4230 valid_rects[0] = valid_rects[1] = client_rect;
4231 apply_window_pos( hwnd, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE |
4232 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW,
4233 &window_rect, &client_rect, valid_rects );
4234 SetThreadDpiAwarenessContext( context );
4237 /***********************************************************************
4238 * show_window
4240 * Implementation of ShowWindow and ShowWindowAsync.
4242 static BOOL show_window( HWND hwnd, INT cmd )
4244 WND *win;
4245 HWND parent;
4246 DPI_AWARENESS_CONTEXT context;
4247 LONG style = get_window_long( hwnd, GWL_STYLE );
4248 BOOL was_visible = (style & WS_VISIBLE) != 0;
4249 BOOL show_flag = TRUE;
4250 RECT newPos = {0, 0, 0, 0};
4251 UINT new_swp, swp = 0;
4253 TRACE( "hwnd=%p, cmd=%d, was_visible %d\n", hwnd, cmd, was_visible );
4255 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
4257 switch(cmd)
4259 case SW_HIDE:
4260 if (!was_visible) goto done;
4261 show_flag = FALSE;
4262 swp |= SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE;
4263 if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4264 break;
4266 case SW_SHOWMINNOACTIVE:
4267 case SW_MINIMIZE:
4268 case SW_FORCEMINIMIZE: /* FIXME: Does not work if thread is hung. */
4269 swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4270 /* fall through */
4271 case SW_SHOWMINIMIZED:
4272 swp |= SWP_SHOWWINDOW | SWP_FRAMECHANGED;
4273 swp |= window_min_maximize( hwnd, cmd, &newPos );
4274 if ((style & WS_MINIMIZE) && was_visible) goto done;
4275 break;
4277 case SW_SHOWMAXIMIZED: /* same as SW_MAXIMIZE */
4278 if (!was_visible) swp |= SWP_SHOWWINDOW;
4279 swp |= SWP_FRAMECHANGED;
4280 swp |= window_min_maximize( hwnd, SW_MAXIMIZE, &newPos );
4281 if ((style & WS_MAXIMIZE) && was_visible) goto done;
4282 break;
4284 case SW_SHOWNA:
4285 swp |= SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
4286 if (style & WS_CHILD) swp |= SWP_NOZORDER;
4287 break;
4289 case SW_SHOW:
4290 if (was_visible) goto done;
4291 swp |= SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
4292 if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4293 break;
4295 case SW_SHOWNOACTIVATE:
4296 swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4297 /* fall through */
4298 case SW_RESTORE:
4299 /* fall through */
4300 case SW_SHOWNORMAL: /* same as SW_NORMAL: */
4301 case SW_SHOWDEFAULT: /* FIXME: should have its own handler */
4302 if (!was_visible) swp |= SWP_SHOWWINDOW;
4303 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
4305 swp |= SWP_FRAMECHANGED;
4306 swp |= window_min_maximize( hwnd, cmd, &newPos );
4308 else
4310 if (was_visible) goto done;
4311 swp |= SWP_NOSIZE | SWP_NOMOVE;
4313 if (style & WS_CHILD && !(swp & SWP_STATECHANGED)) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4314 break;
4316 default:
4317 goto done;
4320 if ((show_flag != was_visible || cmd == SW_SHOWNA) && cmd != SW_SHOWMAXIMIZED && !(swp & SWP_STATECHANGED))
4322 send_message( hwnd, WM_SHOWWINDOW, show_flag, 0 );
4323 if (!is_window( hwnd )) goto done;
4326 if (IsRectEmpty( &newPos )) new_swp = swp;
4327 else if ((new_swp = user_driver->pShowWindow( hwnd, cmd, &newPos, swp )) == ~0)
4329 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) new_swp = swp;
4330 else if (is_iconic( hwnd ) && (newPos.left != -32000 || newPos.top != -32000))
4332 OffsetRect( &newPos, -32000 - newPos.left, -32000 - newPos.top );
4333 new_swp = swp & ~(SWP_NOMOVE | SWP_NOCLIENTMOVE);
4335 else new_swp = swp;
4337 swp = new_swp;
4339 parent = NtUserGetAncestor( hwnd, GA_PARENT );
4340 if (parent && !is_window_visible( parent ) && !(swp & SWP_STATECHANGED))
4342 /* if parent is not visible simply toggle WS_VISIBLE and return */
4343 if (show_flag) set_window_style( hwnd, WS_VISIBLE, 0 );
4344 else set_window_style( hwnd, 0, WS_VISIBLE );
4346 else
4347 NtUserSetWindowPos( hwnd, HWND_TOP, newPos.left, newPos.top,
4348 newPos.right - newPos.left, newPos.bottom - newPos.top, swp );
4350 if (cmd == SW_HIDE)
4352 HWND hFocus;
4354 /* FIXME: This will cause the window to be activated irrespective
4355 * of whether it is owned by the same thread. Has to be done
4356 * asynchronously.
4359 if (hwnd == get_active_window()) activate_other_window( hwnd );
4361 /* Revert focus to parent */
4362 hFocus = get_focus();
4363 if (hwnd == hFocus)
4365 HWND parent = NtUserGetAncestor(hwnd, GA_PARENT);
4366 if (parent == get_desktop_window()) parent = 0;
4367 NtUserSetFocus(parent);
4369 goto done;
4372 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) goto done;
4374 if (win->flags & WIN_NEED_SIZE)
4376 /* should happen only in CreateWindowEx() */
4377 int wParam = SIZE_RESTORED;
4378 RECT client;
4379 LPARAM lparam;
4381 get_window_rects( hwnd, COORDS_PARENT, NULL, &client, get_thread_dpi() );
4382 lparam = MAKELONG( client.right - client.left, client.bottom - client.top );
4383 win->flags &= ~WIN_NEED_SIZE;
4384 if (win->dwStyle & WS_MAXIMIZE) wParam = SIZE_MAXIMIZED;
4385 else if (win->dwStyle & WS_MINIMIZE)
4387 wParam = SIZE_MINIMIZED;
4388 lparam = 0;
4390 release_win_ptr( win );
4392 send_message( hwnd, WM_SIZE, wParam, lparam );
4393 send_message( hwnd, WM_MOVE, 0, MAKELONG( client.left, client.top ));
4395 else release_win_ptr( win );
4397 /* if previous state was minimized Windows sets focus to the window */
4398 if (style & WS_MINIMIZE)
4400 NtUserSetFocus( hwnd );
4401 /* Send a WM_ACTIVATE message for a top level window, even if the window is already active */
4402 if (NtUserGetAncestor( hwnd, GA_ROOT ) == hwnd && !(swp & SWP_NOACTIVATE))
4403 send_message( hwnd, WM_ACTIVATE, WA_ACTIVE, 0 );
4406 done:
4407 SetThreadDpiAwarenessContext( context );
4408 return was_visible;
4411 /***********************************************************************
4412 * NtUserShowWindowAsync (win32u.@)
4414 * doesn't wait; returns immediately.
4415 * used by threads to toggle windows in other (possibly hanging) threads
4417 BOOL WINAPI NtUserShowWindowAsync( HWND hwnd, INT cmd )
4419 HWND full_handle;
4421 if (is_broadcast(hwnd))
4423 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4424 return FALSE;
4427 if ((full_handle = is_current_thread_window( hwnd )))
4428 return show_window( full_handle, cmd );
4430 return NtUserMessageCall( hwnd, WM_WINE_SHOWWINDOW, cmd, 0, 0,
4431 NtUserSendNotifyMessage, FALSE );
4434 /***********************************************************************
4435 * NtUserShowWindow (win32u.@)
4437 BOOL WINAPI NtUserShowWindow( HWND hwnd, INT cmd )
4439 HWND full_handle;
4441 if (is_broadcast(hwnd))
4443 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4444 return FALSE;
4446 if ((full_handle = is_current_thread_window( hwnd )))
4447 return show_window( full_handle, cmd );
4449 if ((cmd == SW_HIDE) && !(get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE))
4450 return FALSE;
4452 if ((cmd == SW_SHOW) && (get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE))
4453 return TRUE;
4455 return send_message( hwnd, WM_WINE_SHOWWINDOW, cmd, 0 );
4458 /* see ShowOwnedPopups */
4459 BOOL show_owned_popups( HWND owner, BOOL show )
4461 int count = 0;
4462 HWND *win_array = list_window_children( 0, get_desktop_window(), NULL, 0 );
4464 if (!win_array) return TRUE;
4466 while (win_array[count]) count++;
4467 while (--count >= 0)
4469 if (get_window_relative( win_array[count], GW_OWNER ) != owner) continue;
4470 if (show)
4472 if (win_get_flags( win_array[count] ) & WIN_NEEDS_SHOW_OWNEDPOPUP)
4473 /* In Windows, ShowOwnedPopups(TRUE) generates
4474 * WM_SHOWWINDOW messages with SW_PARENTOPENING,
4475 * regardless of the state of the owner
4477 send_message( win_array[count], WM_SHOWWINDOW, SW_SHOWNORMAL, SW_PARENTOPENING );
4479 else
4481 if (get_window_long( win_array[count], GWL_STYLE ) & WS_VISIBLE)
4482 /* In Windows, ShowOwnedPopups(FALSE) generates
4483 * WM_SHOWWINDOW messages with SW_PARENTCLOSING,
4484 * regardless of the state of the owner
4486 send_message( win_array[count], WM_SHOWWINDOW, SW_HIDE, SW_PARENTCLOSING );
4490 free( win_array );
4491 return TRUE;
4494 /*******************************************************************
4495 * NtUserFlashWindowEx (win32u.@)
4497 BOOL WINAPI NtUserFlashWindowEx( FLASHWINFO *info )
4499 WND *win;
4501 TRACE( "%p\n", info );
4503 if (!info)
4505 RtlSetLastWin32Error( ERROR_NOACCESS );
4506 return FALSE;
4509 if (!info->hwnd || info->cbSize != sizeof(FLASHWINFO) || !is_window( info->hwnd ))
4511 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4512 return FALSE;
4514 FIXME( "%p - semi-stub\n", info );
4516 if (is_iconic( info->hwnd ))
4518 NtUserRedrawWindow( info->hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_FRAME );
4520 win = get_win_ptr( info->hwnd );
4521 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
4522 if (info->dwFlags & FLASHW_CAPTION && !(win->flags & WIN_NCACTIVATED))
4524 win->flags |= WIN_NCACTIVATED;
4526 else if (!info->dwFlags)
4528 win->flags &= ~WIN_NCACTIVATED;
4530 release_win_ptr( win );
4531 user_driver->pFlashWindowEx( info );
4532 return TRUE;
4534 else
4536 WPARAM wparam;
4537 HWND hwnd = info->hwnd;
4539 win = get_win_ptr( hwnd );
4540 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
4541 hwnd = win->obj.handle; /* make it a full handle */
4543 if (info->dwFlags) wparam = !(win->flags & WIN_NCACTIVATED);
4544 else wparam = (hwnd == NtUserGetForegroundWindow());
4546 release_win_ptr( win );
4548 if (!info->dwFlags || info->dwFlags & FLASHW_CAPTION)
4549 send_message( hwnd, WM_NCACTIVATE, wparam, 0 );
4551 user_driver->pFlashWindowEx( info );
4552 return wparam;
4556 /* see GetWindowContextHelpId */
4557 DWORD get_window_context_help_id( HWND hwnd )
4559 DWORD retval;
4560 WND *win = get_win_ptr( hwnd );
4561 if (!win || win == WND_DESKTOP) return 0;
4562 if (win == WND_OTHER_PROCESS)
4564 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4565 return 0;
4567 retval = win->helpContext;
4568 release_win_ptr( win );
4569 return retval;
4572 /* see SetWindowContextHelpId */
4573 static BOOL set_window_context_help_id( HWND hwnd, DWORD id )
4575 WND *win = get_win_ptr( hwnd );
4576 if (!win || win == WND_DESKTOP) return FALSE;
4577 if (win == WND_OTHER_PROCESS)
4579 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4580 return FALSE;
4582 win->helpContext = id;
4583 release_win_ptr( win );
4584 return TRUE;
4587 /***********************************************************************
4588 * NtUserInternalGetWindowIcon (win32u.@)
4590 HICON WINAPI NtUserInternalGetWindowIcon( HWND hwnd, UINT type )
4592 WND *win = get_win_ptr( hwnd );
4593 HICON ret;
4595 TRACE( "hwnd %p, type %#x\n", hwnd, type );
4597 if (!win)
4599 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
4600 return 0;
4602 if (win == WND_OTHER_PROCESS || win == WND_DESKTOP)
4604 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4605 return 0;
4608 switch (type)
4610 case ICON_BIG:
4611 ret = win->hIcon;
4612 if (!ret) ret = (HICON)get_class_long_ptr( hwnd, GCLP_HICON, FALSE );
4613 break;
4615 case ICON_SMALL:
4616 case ICON_SMALL2:
4617 ret = win->hIconSmall ? win->hIconSmall : win->hIconSmall2;
4618 if (!ret) ret = (HICON)get_class_long_ptr( hwnd, GCLP_HICONSM, FALSE );
4619 if (!ret) ret = (HICON)get_class_long_ptr( hwnd, GCLP_HICON, FALSE );
4620 break;
4622 default:
4623 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4624 release_win_ptr( win );
4625 return 0;
4627 release_win_ptr( win );
4629 if (!ret) ret = LoadImageW( 0, (const WCHAR *)IDI_APPLICATION, IMAGE_ICON,
4630 0, 0, LR_SHARED | LR_DEFAULTSIZE );
4632 return CopyImage( ret, IMAGE_ICON, 0, 0, 0 );
4635 /***********************************************************************
4636 * send_destroy_message
4638 static void send_destroy_message( HWND hwnd )
4640 GUITHREADINFO info;
4642 info.cbSize = sizeof(info);
4643 if (NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ))
4645 if (hwnd == info.hwndCaret) destroy_caret();
4646 if (hwnd == info.hwndActive) activate_other_window( hwnd );
4649 if (hwnd == NtUserGetClipboardOwner()) release_clipboard_owner( hwnd );
4651 send_message( hwnd, WM_DESTROY, 0, 0);
4654 * This WM_DESTROY message can trigger re-entrant calls to DestroyWindow
4655 * make sure that the window still exists when we come back.
4657 if (is_window(hwnd))
4659 HWND *children;
4660 int i;
4662 if (!(children = list_window_children( 0, hwnd, NULL, 0 ))) return;
4664 for (i = 0; children[i]; i++)
4666 if (is_window( children[i] )) send_destroy_message( children[i] );
4668 free( children );
4670 else
4671 WARN( "\tdestroyed itself while in WM_DESTROY!\n" );
4674 /***********************************************************************
4675 * free_window_handle
4677 * Free a window handle.
4679 static void free_window_handle( HWND hwnd )
4681 WND *win;
4683 TRACE( "\n" );
4685 if ((win = get_user_handle_ptr( hwnd, NTUSER_OBJ_WINDOW )) && win != OBJ_OTHER_PROCESS)
4687 SERVER_START_REQ( destroy_window )
4689 req->handle = wine_server_user_handle( hwnd );
4690 wine_server_call( req );
4691 set_user_handle_ptr( hwnd, NULL );
4693 SERVER_END_REQ;
4694 user_unlock();
4695 free( win->pScroll );
4696 free( win->text );
4697 free( win );
4701 /***********************************************************************
4702 * destroy_window
4704 LRESULT destroy_window( HWND hwnd )
4706 struct window_surface *surface;
4707 HMENU menu = 0, sys_menu;
4708 WND *win;
4709 HWND *children;
4711 TRACE( "%p\n", hwnd );
4713 unregister_imm_window( hwnd );
4715 /* free child windows */
4716 if ((children = list_window_children( 0, hwnd, NULL, 0 )))
4718 int i;
4719 for (i = 0; children[i]; i++)
4721 if (is_current_thread_window( children[i] ))
4722 destroy_window( children[i] );
4723 else
4724 NtUserMessageCall( children[i], WM_WINE_DESTROYWINDOW, 0, 0,
4725 0, NtUserSendNotifyMessage, FALSE );
4727 free( children );
4730 /* Unlink now so we won't bother with the children later on */
4731 SERVER_START_REQ( set_parent )
4733 req->handle = wine_server_user_handle( hwnd );
4734 req->parent = 0;
4735 wine_server_call( req );
4737 SERVER_END_REQ;
4739 send_message( hwnd, WM_NCDESTROY, 0, 0 );
4741 /* FIXME: do we need to fake QS_MOUSEMOVE wakebit? */
4743 /* free resources associated with the window */
4745 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
4746 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
4747 menu = (HMENU)win->wIDmenu;
4748 sys_menu = win->hSysMenu;
4749 free_dce( win->dce, hwnd );
4750 win->dce = NULL;
4751 NtUserDestroyCursor( win->hIconSmall2, 0 );
4752 surface = win->surface;
4753 win->surface = NULL;
4754 release_win_ptr( win );
4756 NtUserDestroyMenu( menu );
4757 NtUserDestroyMenu( sys_menu );
4758 if (surface)
4760 register_window_surface( surface, NULL );
4761 window_surface_release( surface );
4764 user_driver->pDestroyWindow( hwnd );
4766 free_window_handle( hwnd );
4767 return 0;
4770 /***********************************************************************
4771 * NtUserDestroyWindow (win32u.@)
4773 BOOL WINAPI NtUserDestroyWindow( HWND hwnd )
4775 BOOL is_child;
4777 if (!(hwnd = is_current_thread_window( hwnd )) || is_desktop_window( hwnd ))
4779 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
4780 return FALSE;
4783 TRACE( "(%p)\n", hwnd );
4785 if (call_hooks( WH_CBT, HCBT_DESTROYWND, (WPARAM)hwnd, 0, 0 )) return FALSE;
4787 if (is_menu_active() == hwnd) NtUserEndMenu();
4789 is_child = (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) != 0;
4791 if (is_child)
4793 if (!is_exiting_thread( GetCurrentThreadId() ))
4794 send_parent_notify( hwnd, WM_DESTROY );
4796 else if (!get_window_relative( hwnd, GW_OWNER ))
4798 call_hooks( WH_SHELL, HSHELL_WINDOWDESTROYED, (WPARAM)hwnd, 0, 0 );
4799 /* FIXME: clean up palette - see "Internals" p.352 */
4802 if (!is_window( hwnd )) return TRUE;
4804 /* Hide the window */
4805 if (get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE)
4807 /* Only child windows receive WM_SHOWWINDOW in DestroyWindow() */
4808 if (is_child)
4809 NtUserShowWindow( hwnd, SW_HIDE );
4810 else
4811 NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE |
4812 SWP_NOZORDER | SWP_NOACTIVATE | SWP_HIDEWINDOW );
4815 if (!is_window( hwnd )) return TRUE;
4817 /* Recursively destroy child windows */
4818 if (!is_child)
4820 for (;;)
4822 BOOL got_one = FALSE;
4823 HWND *children;
4824 unsigned int i;
4826 if (!(children = list_window_children( 0, get_desktop_window(), NULL, 0 ))) break;
4828 for (i = 0; children[i]; i++)
4830 if (get_window_relative( children[i], GW_OWNER ) != hwnd) continue;
4831 if (is_current_thread_window( children[i] ))
4833 NtUserDestroyWindow( children[i] );
4834 got_one = TRUE;
4835 continue;
4837 set_window_owner( children[i], 0 );
4839 free( children );
4840 if (!got_one) break;
4844 send_destroy_message( hwnd );
4845 if (!is_window( hwnd )) return TRUE;
4847 destroy_window( hwnd );
4848 return TRUE;
4851 /*****************************************************************************
4852 * destroy_thread_windows
4854 * Destroy all window owned by the current thread.
4856 void destroy_thread_windows(void)
4858 WND *win, *free_list = NULL;
4859 HANDLE handle = 0;
4861 user_lock();
4862 while ((win = next_process_user_handle_ptr( &handle, NTUSER_OBJ_WINDOW )))
4864 if (win->tid != GetCurrentThreadId()) continue;
4865 free_dce( win->dce, win->obj.handle );
4866 set_user_handle_ptr( handle, NULL );
4867 win->obj.handle = free_list;
4868 free_list = win;
4870 if (free_list)
4872 SERVER_START_REQ( destroy_window )
4874 req->handle = 0; /* destroy all thread windows */
4875 wine_server_call( req );
4877 SERVER_END_REQ;
4879 user_unlock();
4881 while ((win = free_list))
4883 free_list = win->obj.handle;
4884 TRACE( "destroying %p\n", win );
4886 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD && win->wIDmenu)
4887 NtUserDestroyMenu( UlongToHandle(win->wIDmenu) );
4888 if (win->hSysMenu) NtUserDestroyMenu( win->hSysMenu );
4889 if (win->surface)
4891 register_window_surface( win->surface, NULL );
4892 window_surface_release( win->surface );
4894 free( win->pScroll );
4895 free( win->text );
4896 free( win );
4900 /***********************************************************************
4901 * create_window_handle
4903 * Create a window handle with the server.
4905 static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name,
4906 HINSTANCE instance, BOOL ansi,
4907 DWORD style, DWORD ex_style )
4909 DPI_AWARENESS awareness = get_thread_dpi_awareness();
4910 HWND handle = 0, full_parent = 0, full_owner = 0;
4911 struct tagCLASS *class = NULL;
4912 int extra_bytes = 0;
4913 UINT dpi = 0;
4914 WND *win;
4916 SERVER_START_REQ( create_window )
4918 req->parent = wine_server_user_handle( parent );
4919 req->owner = wine_server_user_handle( owner );
4920 req->instance = wine_server_client_ptr( instance );
4921 req->dpi = get_system_dpi();
4922 req->awareness = awareness;
4923 req->style = style;
4924 req->ex_style = ex_style;
4925 if (!(req->atom = get_int_atom_value( name )) && name->Length)
4926 wine_server_add_data( req, name->Buffer, name->Length );
4927 if (!wine_server_call_err( req ))
4929 handle = wine_server_ptr_handle( reply->handle );
4930 full_parent = wine_server_ptr_handle( reply->parent );
4931 full_owner = wine_server_ptr_handle( reply->owner );
4932 extra_bytes = reply->extra;
4933 dpi = reply->dpi;
4934 awareness = reply->awareness;
4935 class = wine_server_get_ptr( reply->class_ptr );
4938 SERVER_END_REQ;
4940 if (!handle)
4942 WARN( "error %d creating window\n", (int)RtlGetLastWin32Error() );
4943 return NULL;
4946 if (!(win = calloc( 1, FIELD_OFFSET(WND, wExtra) + extra_bytes )))
4948 SERVER_START_REQ( destroy_window )
4950 req->handle = wine_server_user_handle( handle );
4951 wine_server_call( req );
4953 SERVER_END_REQ;
4954 RtlSetLastWin32Error( ERROR_NOT_ENOUGH_MEMORY );
4955 return NULL;
4958 if (!parent) /* if parent is 0 we don't have a desktop window yet */
4960 struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
4962 if (name->Buffer == (const WCHAR *)DESKTOP_CLASS_ATOM)
4964 if (!thread_info->top_window) thread_info->top_window = HandleToUlong( full_parent ? full_parent : handle );
4965 else assert( full_parent == UlongToHandle( thread_info->top_window ));
4966 if (!thread_info->top_window) ERR_(win)( "failed to create desktop window\n" );
4967 else user_driver->pSetDesktopWindow( UlongToHandle( thread_info->top_window ));
4968 register_builtin_classes();
4970 else /* HWND_MESSAGE parent */
4972 if (!thread_info->msg_window && !full_parent)
4973 thread_info->msg_window = HandleToUlong( handle );
4977 user_lock();
4979 win->obj.handle = handle;
4980 win->obj.type = NTUSER_OBJ_WINDOW;
4981 win->parent = full_parent;
4982 win->owner = full_owner;
4983 win->class = class;
4984 win->winproc = get_class_winproc( class );
4985 win->cbWndExtra = extra_bytes;
4986 win->dpi = dpi;
4987 win->dpi_awareness = awareness;
4988 set_user_handle_ptr( handle, &win->obj );
4989 if (is_winproc_unicode( win->winproc, !ansi )) win->flags |= WIN_ISUNICODE;
4990 return win;
4993 static BOOL is_default_coord( int x )
4995 return x == CW_USEDEFAULT || x == 0x8000;
4998 /***********************************************************************
4999 * fix_cs_coordinates
5001 * Fix the coordinates and return default show mode in sw.
5003 static void fix_cs_coordinates( CREATESTRUCTW *cs, INT *sw )
5005 if (cs->style & (WS_CHILD | WS_POPUP))
5007 if (is_default_coord(cs->x)) cs->x = cs->y = 0;
5008 if (is_default_coord(cs->cx)) cs->cx = cs->cy = 0;
5010 else /* overlapped window */
5012 RTL_USER_PROCESS_PARAMETERS *params = NtCurrentTeb()->Peb->ProcessParameters;
5013 HMONITOR monitor;
5014 MONITORINFO mon_info;
5016 if (!is_default_coord( cs->x ) && !is_default_coord( cs->cx ) && !is_default_coord( cs->cy ))
5017 return;
5019 monitor = monitor_from_window( cs->hwndParent, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
5020 mon_info.cbSize = sizeof(mon_info);
5021 get_monitor_info( monitor, &mon_info );
5023 if (is_default_coord( cs->x ))
5025 if (!is_default_coord( cs->y )) *sw = cs->y;
5026 cs->x = (params->dwFlags & STARTF_USEPOSITION) ? params->dwX : mon_info.rcWork.left;
5027 cs->y = (params->dwFlags & STARTF_USEPOSITION) ? params->dwY : mon_info.rcWork.top;
5030 if (is_default_coord( cs->cx ))
5032 if (params->dwFlags & STARTF_USESIZE)
5034 cs->cx = params->dwXSize;
5035 cs->cy = params->dwYSize;
5037 else
5039 cs->cx = (mon_info.rcWork.right - mon_info.rcWork.left) * 3 / 4 - cs->x;
5040 cs->cy = (mon_info.rcWork.bottom - mon_info.rcWork.top) * 3 / 4 - cs->y;
5043 /* neither x nor cx are default. Check the y values.
5044 * In the trace we see Outlook and Outlook Express using
5045 * cy set to CW_USEDEFAULT when opening the address book.
5047 else if (is_default_coord( cs->cy ))
5049 FIXME( "Strange use of CW_USEDEFAULT in cy\n" );
5050 cs->cy = (mon_info.rcWork.bottom - mon_info.rcWork.top) * 3 / 4 - cs->y;
5055 /***********************************************************************
5056 * map_dpi_create_struct
5058 static void map_dpi_create_struct( CREATESTRUCTW *cs, UINT dpi_from, UINT dpi_to )
5060 if (!dpi_from && !dpi_to) return;
5061 if (!dpi_from || !dpi_to)
5063 POINT pt = { cs->x, cs->y };
5064 UINT mon_dpi = get_monitor_dpi( monitor_from_point( pt, MONITOR_DEFAULTTONEAREST, dpi_from ));
5065 if (!dpi_from) dpi_from = mon_dpi;
5066 else dpi_to = mon_dpi;
5068 if (dpi_from == dpi_to) return;
5069 cs->x = muldiv( cs->x, dpi_to, dpi_from );
5070 cs->y = muldiv( cs->y, dpi_to, dpi_from );
5071 cs->cx = muldiv( cs->cx, dpi_to, dpi_from );
5072 cs->cy = muldiv( cs->cy, dpi_to, dpi_from );
5075 /***********************************************************************
5076 * NtUserCreateWindowEx (win32u.@)
5078 HWND WINAPI NtUserCreateWindowEx( DWORD ex_style, UNICODE_STRING *class_name,
5079 UNICODE_STRING *version, UNICODE_STRING *window_name,
5080 DWORD style, INT x, INT y, INT cx, INT cy,
5081 HWND parent, HMENU menu, HINSTANCE instance, void *params,
5082 DWORD flags, HINSTANCE client_instance, DWORD unk, BOOL ansi )
5084 UINT win_dpi, thread_dpi = get_thread_dpi();
5085 DPI_AWARENESS_CONTEXT context;
5086 CBT_CREATEWNDW cbtc;
5087 HWND hwnd, owner = 0;
5088 CREATESTRUCTW cs;
5089 INT sw = SW_SHOW;
5090 RECT rect;
5091 WND *win;
5093 static const WCHAR messageW[] = {'M','e','s','s','a','g','e'};
5095 cs.lpCreateParams = params;
5096 cs.hInstance = client_instance ? client_instance : instance;
5097 cs.hMenu = menu;
5098 cs.hwndParent = parent;
5099 cs.style = style;
5100 cs.dwExStyle = ex_style;
5101 cs.lpszName = window_name ? window_name->Buffer : NULL;
5102 cs.lpszClass = class_name ? class_name->Buffer : NULL;
5103 cs.x = x;
5104 cs.y = y;
5105 cs.cx = cx;
5106 cs.cy = cy;
5108 /* Find the parent window */
5109 if (parent == HWND_MESSAGE)
5111 cs.hwndParent = parent = get_hwnd_message_parent();
5113 else if (parent)
5115 if ((cs.style & (WS_CHILD|WS_POPUP)) != WS_CHILD)
5117 owner = parent;
5118 parent = get_desktop_window();
5120 else
5122 DWORD parent_style = get_window_long( parent, GWL_EXSTYLE );
5123 if ((parent_style & WS_EX_LAYOUTRTL) && !(parent_style & WS_EX_NOINHERITLAYOUT))
5124 cs.dwExStyle |= WS_EX_LAYOUTRTL;
5127 else
5129 if ((cs.style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
5131 WARN( "No parent for child window\n" );
5132 RtlSetLastWin32Error( ERROR_TLW_WITH_WSCHILD );
5133 return 0; /* WS_CHILD needs a parent, but WS_POPUP doesn't */
5136 /* are we creating the desktop or HWND_MESSAGE parent itself? */
5137 if (class_name->Buffer != (LPCWSTR)DESKTOP_CLASS_ATOM &&
5138 (class_name->Length != sizeof(messageW) ||
5139 wcsnicmp( class_name->Buffer, messageW, ARRAYSIZE(messageW) )))
5141 if (get_process_layout() & LAYOUT_RTL) cs.dwExStyle |= WS_EX_LAYOUTRTL;
5142 parent = get_desktop_window();
5146 fix_cs_coordinates( &cs, &sw );
5147 cs.dwExStyle = fix_exstyle( cs.style, cs.dwExStyle );
5149 /* Create the window structure */
5151 style = cs.style & ~WS_VISIBLE;
5152 ex_style = cs.dwExStyle & ~WS_EX_LAYERED;
5153 if (!(win = create_window_handle( parent, owner, class_name, instance, ansi, style, ex_style )))
5154 return 0;
5155 hwnd = win->obj.handle;
5157 /* Fill the window structure */
5159 win->tid = GetCurrentThreadId();
5160 win->hInstance = cs.hInstance;
5161 win->text = NULL;
5162 win->dwStyle = style;
5163 win->dwExStyle = ex_style;
5164 win->wIDmenu = 0;
5165 win->helpContext = 0;
5166 win->pScroll = NULL;
5167 win->userdata = 0;
5168 win->hIcon = 0;
5169 win->hIconSmall = 0;
5170 win->hIconSmall2 = 0;
5171 win->hSysMenu = 0;
5173 win->min_pos.x = win->min_pos.y = -1;
5174 win->max_pos.x = win->max_pos.y = -1;
5175 SetRect( &win->normal_rect, cs.x, cs.y, cs.x + cs.cx, cs.y + cs.cy );
5177 if (win->dwStyle & WS_SYSMENU) NtUserSetSystemMenu( hwnd, 0 );
5179 win->imc = get_default_input_context();
5181 /* call the WH_CBT hook */
5183 release_win_ptr( win );
5184 cbtc.hwndInsertAfter = HWND_TOP;
5185 cbtc.lpcs = &cs;
5186 if (call_hooks( WH_CBT, HCBT_CREATEWND, (WPARAM)hwnd, (LPARAM)&cbtc, sizeof(cbtc) ))
5188 free_window_handle( hwnd );
5189 return 0;
5191 if (!(win = get_win_ptr( hwnd ))) return 0;
5194 * Correct the window styles.
5196 * It affects only the style loaded into the WND structure.
5199 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
5201 win->dwStyle |= WS_CLIPSIBLINGS;
5202 if (!(win->dwStyle & WS_POPUP)) win->dwStyle |= WS_CAPTION;
5205 win->dwExStyle = cs.dwExStyle;
5206 /* WS_EX_WINDOWEDGE depends on some other styles */
5207 if ((win->dwStyle & (WS_DLGFRAME | WS_THICKFRAME)) &&
5208 !(win->dwStyle & (WS_CHILD | WS_POPUP)))
5209 win->dwExStyle |= WS_EX_WINDOWEDGE;
5211 if (!(win->dwStyle & (WS_CHILD | WS_POPUP))) win->flags |= WIN_NEED_SIZE;
5213 SERVER_START_REQ( set_window_info )
5215 req->handle = wine_server_user_handle( hwnd );
5216 req->flags = SET_WIN_STYLE | SET_WIN_EXSTYLE | SET_WIN_INSTANCE | SET_WIN_UNICODE;
5217 req->style = win->dwStyle;
5218 req->ex_style = win->dwExStyle;
5219 req->instance = wine_server_client_ptr( win->hInstance );
5220 req->is_unicode = (win->flags & WIN_ISUNICODE) != 0;
5221 req->extra_offset = -1;
5222 wine_server_call( req );
5224 SERVER_END_REQ;
5226 /* Set the window menu */
5228 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
5230 if (cs.hMenu && !set_window_menu( hwnd, cs.hMenu ))
5232 release_win_ptr( win );
5233 free_window_handle( hwnd );
5234 return 0;
5237 else NtUserSetWindowLongPtr( hwnd, GWLP_ID, (ULONG_PTR)cs.hMenu, FALSE );
5239 win_dpi = win->dpi;
5240 release_win_ptr( win );
5242 if (parent) map_dpi_create_struct( &cs, thread_dpi, win_dpi );
5244 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
5246 /* send the WM_GETMINMAXINFO message and fix the size if needed */
5248 cx = cs.cx;
5249 cy = cs.cy;
5250 if ((cs.style & WS_THICKFRAME) || !(cs.style & (WS_POPUP | WS_CHILD)))
5252 MINMAXINFO info = get_min_max_info( hwnd );
5253 cx = max( min( cx, info.ptMaxTrackSize.x ), info.ptMinTrackSize.x );
5254 cy = max( min( cy, info.ptMaxTrackSize.y ), info.ptMinTrackSize.y );
5257 if (cx < 0) cx = 0;
5258 if (cy < 0) cy = 0;
5259 SetRect( &rect, cs.x, cs.y, cs.x + cx, cs.y + cy );
5260 /* check for wraparound */
5261 if (cs.x > 0x7fffffff - cx) rect.right = 0x7fffffff;
5262 if (cs.y > 0x7fffffff - cy) rect.bottom = 0x7fffffff;
5263 if (!apply_window_pos( hwnd, 0, SWP_NOZORDER | SWP_NOACTIVATE, &rect, &rect, NULL )) goto failed;
5265 /* send WM_NCCREATE */
5267 TRACE( "hwnd %p cs %d,%d %dx%d %s\n", hwnd, cs.x, cs.y, cs.cx, cs.cy, wine_dbgstr_rect(&rect) );
5268 if (!send_message_timeout( hwnd, WM_NCCREATE, 0, (LPARAM)&cs, SMTO_NORMAL, 0, ansi ))
5270 WARN( "%p: aborted by WM_NCCREATE\n", hwnd );
5271 goto failed;
5274 /* create default IME window */
5276 if (!is_desktop_window( hwnd ) && parent != get_hwnd_message_parent() &&
5277 register_imm_window( hwnd ))
5279 TRACE( "register IME window for %p\n", hwnd );
5280 win_set_flags( hwnd, WIN_HAS_IME_WIN, 0 );
5283 /* send WM_NCCALCSIZE */
5285 if (get_window_rects( hwnd, COORDS_PARENT, &rect, NULL, win_dpi ))
5287 /* yes, even if the CBT hook was called with HWND_TOP */
5288 HWND insert_after = (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) ? HWND_BOTTOM : HWND_TOP;
5289 RECT client_rect = rect;
5291 /* the rectangle is in screen coords for WM_NCCALCSIZE when wparam is FALSE */
5292 map_window_points( parent, 0, (POINT *)&client_rect, 2, win_dpi );
5293 send_message( hwnd, WM_NCCALCSIZE, FALSE, (LPARAM)&client_rect );
5294 map_window_points( 0, parent, (POINT *)&client_rect, 2, win_dpi );
5295 apply_window_pos( hwnd, insert_after, SWP_NOACTIVATE, &rect, &client_rect, NULL );
5297 else goto failed;
5299 /* send WM_CREATE */
5300 if (send_message_timeout( hwnd, WM_CREATE, 0, (LPARAM)&cs, SMTO_NORMAL, 0, ansi ) == -1)
5301 goto failed;
5303 /* call the driver */
5305 if (!user_driver->pCreateWindow( hwnd )) goto failed;
5307 NtUserNotifyWinEvent( EVENT_OBJECT_CREATE, hwnd, OBJID_WINDOW, 0 );
5309 /* send the size messages */
5311 if (!(win_get_flags( hwnd ) & WIN_NEED_SIZE))
5313 get_window_rects( hwnd, COORDS_PARENT, NULL, &rect, win_dpi );
5314 send_message( hwnd, WM_SIZE, SIZE_RESTORED,
5315 MAKELONG(rect.right-rect.left, rect.bottom-rect.top));
5316 send_message( hwnd, WM_MOVE, 0, MAKELONG( rect.left, rect.top ) );
5319 /* Show the window, maximizing or minimizing if needed */
5321 style = set_window_style( hwnd, 0, WS_MAXIMIZE | WS_MINIMIZE );
5322 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
5324 RECT new_pos;
5325 UINT sw_flags = (style & WS_MINIMIZE) ? SW_MINIMIZE : SW_MAXIMIZE;
5327 sw_flags = window_min_maximize( hwnd, sw_flags, &new_pos );
5328 sw_flags |= SWP_FRAMECHANGED; /* Frame always gets changed */
5329 if (!(style & WS_VISIBLE) || (style & WS_CHILD) || get_active_window())
5330 sw_flags |= SWP_NOACTIVATE;
5331 NtUserSetWindowPos( hwnd, 0, new_pos.left, new_pos.top, new_pos.right - new_pos.left,
5332 new_pos.bottom - new_pos.top, sw_flags );
5335 /* Notify the parent window only */
5337 send_parent_notify( hwnd, WM_CREATE );
5338 if (!is_window( hwnd ))
5340 SetThreadDpiAwarenessContext( context );
5341 return 0;
5344 if (parent == get_desktop_window())
5345 NtUserPostMessage( parent, WM_PARENTNOTIFY, WM_CREATE, (LPARAM)hwnd );
5347 if (cs.style & WS_VISIBLE)
5349 if (cs.style & WS_MAXIMIZE)
5350 sw = SW_SHOW;
5351 else if (cs.style & WS_MINIMIZE)
5352 sw = SW_SHOWMINIMIZED;
5354 NtUserShowWindow( hwnd, sw );
5355 if (cs.dwExStyle & WS_EX_MDICHILD)
5357 send_message( cs.hwndParent, WM_MDIREFRESHMENU, 0, 0 );
5358 /* ShowWindow won't activate child windows */
5359 NtUserSetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE );
5363 /* Call WH_SHELL hook */
5365 if (!(get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) && !get_window_relative( hwnd, GW_OWNER ))
5366 call_hooks( WH_SHELL, HSHELL_WINDOWCREATED, (WPARAM)hwnd, 0, 0 );
5368 TRACE( "created window %p\n", hwnd );
5369 SetThreadDpiAwarenessContext( context );
5370 return hwnd;
5372 failed:
5373 destroy_window( hwnd );
5374 SetThreadDpiAwarenessContext( context );
5375 return 0;
5378 static void *get_dialog_info( HWND hwnd )
5380 WND *win;
5381 void *ret;
5383 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP)
5385 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
5386 return NULL;
5389 ret = win->dlgInfo;
5390 release_win_ptr( win );
5391 return ret;
5394 static BOOL set_dialog_info( HWND hwnd, void *info )
5396 WND *win;
5398 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
5399 win->dlgInfo = info;
5400 release_win_ptr( win );
5401 return TRUE;
5404 /*****************************************************************************
5405 * NtUserCallHwnd (win32u.@)
5407 ULONG_PTR WINAPI NtUserCallHwnd( HWND hwnd, DWORD code )
5409 switch (code)
5411 case NtUserCallHwnd_ActivateOtherWindow:
5412 activate_other_window( hwnd );
5413 return 0;
5415 case NtUserCallHwnd_ArrangeIconicWindows:
5416 return arrange_iconic_windows( hwnd );
5418 case NtUserCallHwnd_DrawMenuBar:
5419 return draw_menu_bar( hwnd );
5421 case NtUserCallHwnd_GetDefaultImeWindow:
5422 return HandleToUlong( get_default_ime_window( hwnd ));
5424 case NtUserCallHwnd_GetDpiForWindow:
5425 return get_dpi_for_window( hwnd );
5427 case NtUserCallHwnd_GetParent:
5428 return HandleToUlong( get_parent( hwnd ));
5430 case NtUserCallHwnd_GetDialogInfo:
5431 return (ULONG_PTR)get_dialog_info( hwnd );
5433 case NtUserCallHwnd_GetMDIClientInfo:
5434 if (!(win_get_flags( hwnd ) & WIN_ISMDICLIENT)) return 0;
5435 return get_window_long_ptr( hwnd, sizeof(void *), FALSE );
5437 case NtUserCallHwnd_GetWindowContextHelpId:
5438 return get_window_context_help_id( hwnd );
5440 case NtUserCallHwnd_GetWindowDpiAwarenessContext:
5441 return (ULONG_PTR)get_window_dpi_awareness_context( hwnd );
5443 case NtUserCallHwnd_GetWindowInputContext:
5444 return HandleToUlong( get_window_input_context( hwnd ));
5446 case NtUserCallHwnd_GetWindowSysSubMenu:
5447 return HandleToUlong( get_window_sys_sub_menu( hwnd ));
5449 case NtUserCallHwnd_GetWindowTextLength:
5450 return get_server_window_text( hwnd, NULL, 0 );
5452 case NtUserCallHwnd_IsWindow:
5453 return is_window( hwnd );
5455 case NtUserCallHwnd_IsWindowEnabled:
5456 return is_window_enabled( hwnd );
5458 case NtUserCallHwnd_IsWindowUnicode:
5459 return is_window_unicode( hwnd );
5461 case NtUserCallHwnd_IsWindowVisible:
5462 return is_window_visible( hwnd );
5464 case NtUserCallHwnd_SetForegroundWindow:
5465 return set_foreground_window( hwnd, FALSE );
5467 case NtUserCallHwnd_SetProgmanWindow:
5468 return HandleToUlong( set_progman_window( hwnd ));
5470 case NtUserCallHwnd_SetTaskmanWindow:
5471 return HandleToUlong( set_taskman_window( hwnd ));
5473 /* temporary exports */
5474 case NtUserGetFullWindowHandle:
5475 return HandleToUlong( get_full_window_handle( hwnd ));
5477 case NtUserIsCurrehtProcessWindow:
5478 return HandleToUlong( is_current_process_window( hwnd ));
5480 case NtUserIsCurrehtThreadWindow:
5481 return HandleToUlong( is_current_thread_window( hwnd ));
5483 default:
5484 FIXME( "invalid code %u\n", (int)code );
5485 return 0;
5489 /*****************************************************************************
5490 * NtUserCallHwndParam (win32u.@)
5492 ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code )
5494 switch (code)
5496 case NtUserCallHwndParam_ClientToScreen:
5497 return client_to_screen( hwnd, (POINT *)param );
5499 case NtUserCallHwndParam_EnableWindow:
5500 return enable_window( hwnd, param );
5502 case NtUserCallHwndParam_GetChildRect:
5503 return get_window_rects( hwnd, COORDS_PARENT, (RECT *)param, NULL, get_thread_dpi() );
5505 case NtUserCallHwndParam_GetClassLongA:
5506 return get_class_long( hwnd, param, TRUE );
5508 case NtUserCallHwndParam_GetClassLongW:
5509 return get_class_long( hwnd, param, FALSE );
5511 case NtUserCallHwndParam_GetClassLongPtrA:
5512 return get_class_long_ptr( hwnd, param, TRUE );
5514 case NtUserCallHwndParam_GetClassLongPtrW:
5515 return get_class_long_ptr( hwnd, param, FALSE );
5517 case NtUserCallHwndParam_GetClassWord:
5518 return get_class_word( hwnd, param );
5520 case NtUserCallHwndParam_GetClientRect:
5521 return get_client_rect( hwnd, (RECT *)param );
5523 case NtUserCallHwndParam_GetScrollInfo:
5525 struct get_scroll_info_params *params = (void *)param;
5526 return get_scroll_info( hwnd, params->bar, params->info );
5529 case NtUserCallHwndParam_GetWindowInfo:
5530 return get_window_info( hwnd, (WINDOWINFO *)param );
5532 case NtUserCallHwndParam_GetWindowLongA:
5533 return get_window_long_size( hwnd, param, sizeof(LONG), TRUE );
5535 case NtUserCallHwndParam_GetWindowLongW:
5536 return get_window_long( hwnd, param );
5538 case NtUserCallHwndParam_GetWindowLongPtrA:
5539 return get_window_long_ptr( hwnd, param, TRUE );
5541 case NtUserCallHwndParam_GetWindowLongPtrW:
5542 return get_window_long_ptr( hwnd, param, FALSE );
5544 case NtUserCallHwndParam_GetWindowRect:
5545 return get_window_rect( hwnd, (RECT *)param, get_thread_dpi() );
5547 case NtUserCallHwndParam_GetWindowRelative:
5548 return HandleToUlong( get_window_relative( hwnd, param ));
5550 case NtUserCallHwndParam_GetWindowThread:
5551 return get_window_thread( hwnd, (DWORD *)param );
5553 case NtUserCallHwndParam_GetWindowWord:
5554 return get_window_word( hwnd, param );
5556 case NtUserCallHwndParam_IsChild:
5557 return is_child( hwnd, UlongToHandle(param) );
5559 case NtUserCallHwndParam_KillSystemTimer:
5560 return kill_system_timer( hwnd, param );
5562 case NtUserCallHwndParam_MapWindowPoints:
5564 struct map_window_points_params *params = (void *)param;
5565 return map_window_points( hwnd, params->hwnd_to, params->points, params->count,
5566 get_thread_dpi() );
5569 case NtUserCallHwndParam_MirrorRgn:
5570 return mirror_window_region( hwnd, UlongToHandle(param) );
5572 case NtUserCallHwndParam_MonitorFromWindow:
5573 return HandleToUlong( monitor_from_window( hwnd, param, get_thread_dpi() ));
5575 case NtUserCallHwndParam_ScreenToClient:
5576 return screen_to_client( hwnd, (POINT *)param );
5578 case NtUserCallHwndParam_SetDialogInfo:
5579 return set_dialog_info( hwnd, (void *)param );
5581 case NtUserCallHwndParam_SetMDIClientInfo:
5582 NtUserSetWindowLongPtr( hwnd, sizeof(void *), param, FALSE );
5583 return win_set_flags( hwnd, WIN_ISMDICLIENT, 0 );
5585 case NtUserCallHwndParam_SetWindowContextHelpId:
5586 return set_window_context_help_id( hwnd, param );
5588 case NtUserCallHwndParam_ShowOwnedPopups:
5589 return show_owned_popups( hwnd, param );
5591 /* temporary exports */
5592 case NtUserSetWindowStyle:
5594 STYLESTRUCT *style = (void *)param;
5595 return set_window_style( hwnd, style->styleNew, style->styleOld );
5598 default:
5599 FIXME( "invalid code %u\n", (int)code );
5600 return 0;
5604 /*******************************************************************
5605 * NtUserDragDetect (win32u.@)
5607 BOOL WINAPI NtUserDragDetect( HWND hwnd, int x, int y )
5609 WORD width, height;
5610 RECT rect;
5611 MSG msg;
5613 TRACE( "%p (%d,%d)\n", hwnd, x, y );
5615 if (!(NtUserGetKeyState( VK_LBUTTON ) & 0x8000)) return FALSE;
5617 width = get_system_metrics( SM_CXDRAG );
5618 height = get_system_metrics( SM_CYDRAG );
5619 SetRect( &rect, x - width, y - height, x + width, y + height );
5621 NtUserSetCapture( hwnd );
5623 for (;;)
5625 while (NtUserPeekMessage( &msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE ))
5627 if (msg.message == WM_LBUTTONUP)
5629 release_capture();
5630 return FALSE;
5632 if (msg.message == WM_MOUSEMOVE)
5634 POINT tmp;
5635 tmp.x = (short)LOWORD( msg.lParam );
5636 tmp.y = (short)HIWORD( msg.lParam );
5637 if (!PtInRect( &rect, tmp ))
5639 release_capture();
5640 return TRUE;
5644 NtUserMsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, 0 );
5646 return FALSE;
5649 /*******************************************************************
5650 * NtUserDragObject (win32u.@)
5652 DWORD WINAPI NtUserDragObject( HWND parent, HWND hwnd, UINT fmt, ULONG_PTR data, HCURSOR cursor )
5654 FIXME( "%p, %p, %u, %#lx, %p stub!\n", parent, hwnd, fmt, data, cursor );
5656 return 0;
5660 HWND get_shell_window(void)
5662 HWND hwnd = 0;
5664 SERVER_START_REQ(set_global_windows)
5666 req->flags = 0;
5667 if (!wine_server_call_err(req))
5668 hwnd = wine_server_ptr_handle( reply->old_shell_window );
5670 SERVER_END_REQ;
5672 return hwnd;
5675 /***********************************************************************
5676 * NtUserSetShellWindowEx (win32u.@)
5678 BOOL WINAPI NtUserSetShellWindowEx( HWND shell, HWND list_view )
5680 BOOL ret;
5682 /* shell = Progman[Program Manager]
5683 * |-> SHELLDLL_DefView
5684 * list_view = | |-> SysListView32
5685 * | | |-> tooltips_class32
5686 * | |
5687 * | |-> SysHeader32
5689 * |-> ProxyTarget
5692 if (get_shell_window())
5693 return FALSE;
5695 if (get_window_long( shell, GWL_EXSTYLE ) & WS_EX_TOPMOST)
5696 return FALSE;
5698 if (list_view != shell && (get_window_long( list_view, GWL_EXSTYLE ) & WS_EX_TOPMOST))
5699 return FALSE;
5701 if (list_view && list_view != shell)
5702 NtUserSetWindowPos( list_view, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
5704 NtUserSetWindowPos( shell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
5706 SERVER_START_REQ(set_global_windows)
5708 req->flags = SET_GLOBAL_SHELL_WINDOWS;
5709 req->shell_window = wine_server_user_handle( shell );
5710 req->shell_listview = wine_server_user_handle( list_view );
5711 ret = !wine_server_call_err(req);
5713 SERVER_END_REQ;
5714 return ret;
5717 HWND get_progman_window(void)
5719 HWND ret = 0;
5721 SERVER_START_REQ(set_global_windows)
5723 req->flags = 0;
5724 if (!wine_server_call_err(req))
5725 ret = wine_server_ptr_handle( reply->old_progman_window );
5727 SERVER_END_REQ;
5728 return ret;
5731 HWND set_progman_window( HWND hwnd )
5733 SERVER_START_REQ(set_global_windows)
5735 req->flags = SET_GLOBAL_PROGMAN_WINDOW;
5736 req->progman_window = wine_server_user_handle( hwnd );
5737 if (wine_server_call_err( req )) hwnd = 0;
5739 SERVER_END_REQ;
5740 return hwnd;
5743 HWND get_taskman_window(void)
5745 HWND ret = 0;
5747 SERVER_START_REQ(set_global_windows)
5749 req->flags = 0;
5750 if (!wine_server_call_err(req))
5751 ret = wine_server_ptr_handle( reply->old_taskman_window );
5753 SERVER_END_REQ;
5754 return ret;
5757 HWND set_taskman_window( HWND hwnd )
5759 /* hwnd = MSTaskSwWClass
5760 * |-> SysTabControl32
5762 SERVER_START_REQ(set_global_windows)
5764 req->flags = SET_GLOBAL_TASKMAN_WINDOW;
5765 req->taskman_window = wine_server_user_handle( hwnd );
5766 if (wine_server_call_err( req )) hwnd = 0;
5768 SERVER_END_REQ;
5769 return hwnd;