Sweden-Number/dlls/win32u/window.c

1444 lines
42 KiB
C
Raw Normal View History

/*
* Window related functions
*
* Copyright 1993, 1994 Alexandre Julliard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#if 0
#pragma makedep unix
#endif
#include <pthread.h>
#include <assert.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "ntgdi_private.h"
#include "ntuser_private.h"
#include "wine/server.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(win);
#define NB_USER_HANDLES ((LAST_USER_HANDLE - FIRST_USER_HANDLE + 1) >> 1)
#define USER_HANDLE_TO_INDEX(hwnd) ((LOWORD(hwnd) - FIRST_USER_HANDLE) >> 1)
static void *user_handles[NB_USER_HANDLES];
static struct list window_surfaces = LIST_INIT( window_surfaces );
static pthread_mutex_t surfaces_lock = PTHREAD_MUTEX_INITIALIZER;
/***********************************************************************
* alloc_user_handle
*/
HANDLE alloc_user_handle( struct user_object *ptr, unsigned int type )
{
HANDLE handle = 0;
SERVER_START_REQ( alloc_user_handle )
{
if (!wine_server_call_err( req )) handle = wine_server_ptr_handle( reply->handle );
}
SERVER_END_REQ;
if (handle)
{
UINT index = USER_HANDLE_TO_INDEX( handle );
assert( index < NB_USER_HANDLES );
ptr->handle = handle;
ptr->type = type;
InterlockedExchangePointer( &user_handles[index], ptr );
}
return handle;
}
/***********************************************************************
* get_user_handle_ptr
*/
void *get_user_handle_ptr( HANDLE handle, unsigned int type )
{
struct user_object *ptr;
WORD index = USER_HANDLE_TO_INDEX( handle );
if (index >= NB_USER_HANDLES) return NULL;
user_lock();
if ((ptr = user_handles[index]))
{
if (ptr->type == type &&
((UINT)(UINT_PTR)ptr->handle == (UINT)(UINT_PTR)handle ||
!HIWORD(handle) || HIWORD(handle) == 0xffff))
return ptr;
ptr = NULL;
}
else ptr = OBJ_OTHER_PROCESS;
user_unlock();
return ptr;
}
/***********************************************************************
* set_user_handle_ptr
*/
void set_user_handle_ptr( HANDLE handle, struct user_object *ptr )
{
WORD index = USER_HANDLE_TO_INDEX(handle);
assert( index < NB_USER_HANDLES );
InterlockedExchangePointer( &user_handles[index], ptr );
}
/***********************************************************************
* release_user_handle_ptr
*/
void release_user_handle_ptr( void *ptr )
{
assert( ptr && ptr != OBJ_OTHER_PROCESS );
user_unlock();
}
/***********************************************************************
* free_user_handle
*/
void *free_user_handle( HANDLE handle, unsigned int type )
{
struct user_object *ptr;
WORD index = USER_HANDLE_TO_INDEX( handle );
if ((ptr = get_user_handle_ptr( handle, type )) && ptr != OBJ_OTHER_PROCESS)
{
SERVER_START_REQ( free_user_handle )
{
req->handle = wine_server_user_handle( handle );
if (wine_server_call( req )) ptr = NULL;
else InterlockedCompareExchangePointer( &user_handles[index], NULL, ptr );
}
SERVER_END_REQ;
user_unlock();
}
return ptr;
}
/***********************************************************************
* next_thread_window
*/
WND *next_thread_window_ptr( HWND *hwnd )
{
struct user_object *ptr;
WND *win;
WORD index = *hwnd ? USER_HANDLE_TO_INDEX( *hwnd ) + 1 : 0;
while (index < NB_USER_HANDLES)
{
if (!(ptr = user_handles[index++])) continue;
if (ptr->type != NTUSER_OBJ_WINDOW) continue;
win = (WND *)ptr;
if (win->tid != GetCurrentThreadId()) continue;
*hwnd = ptr->handle;
return win;
}
return NULL;
}
/*******************************************************************
* get_hwnd_message_parent
*
* Return the parent for HWND_MESSAGE windows.
*/
static HWND get_hwnd_message_parent(void)
{
struct user_thread_info *thread_info = get_user_thread_info();
if (!thread_info->msg_window) get_desktop_window(); /* trigger creation */
return thread_info->msg_window;
}
/***********************************************************************
* get_full_window_handle
*
* Convert a possibly truncated window handle to a full 32-bit handle.
*/
static HWND get_full_window_handle( HWND hwnd )
{
WND *win;
if (!hwnd || (ULONG_PTR)hwnd >> 16) return hwnd;
if (LOWORD(hwnd) <= 1 || LOWORD(hwnd) == 0xffff) return hwnd;
/* do sign extension for -2 and -3 */
if (LOWORD(hwnd) >= (WORD)-3) return (HWND)(LONG_PTR)(INT16)LOWORD(hwnd);
if (!(win = get_win_ptr( hwnd ))) return hwnd;
if (win == WND_DESKTOP)
{
if (LOWORD(hwnd) == LOWORD(get_desktop_window())) return get_desktop_window();
else return get_hwnd_message_parent();
}
if (win != WND_OTHER_PROCESS)
{
hwnd = win->obj.handle;
release_win_ptr( win );
}
else /* may belong to another process */
{
SERVER_START_REQ( get_window_info )
{
req->handle = wine_server_user_handle( hwnd );
if (!wine_server_call_err( req )) hwnd = wine_server_ptr_handle( reply->full_handle );
}
SERVER_END_REQ;
}
return hwnd;
}
/*******************************************************************
* register_window_surface
*
* Register a window surface in the global list, possibly replacing another one.
*/
void register_window_surface( struct window_surface *old, struct window_surface *new )
{
if (old == new) return;
pthread_mutex_lock( &surfaces_lock );
if (old) list_remove( &old->entry );
if (new) list_add_tail( &window_surfaces, &new->entry );
pthread_mutex_unlock( &surfaces_lock );
}
/*******************************************************************
* flush_window_surfaces
*
* Flush pending output from all window surfaces.
*/
void flush_window_surfaces( BOOL idle )
{
static DWORD last_idle;
DWORD now;
struct window_surface *surface;
pthread_mutex_lock( &surfaces_lock );
now = NtGetTickCount();
if (idle) last_idle = now;
/* if not idle, we only flush if there's evidence that the app never goes idle */
else if ((int)(now - last_idle) < 50) goto done;
LIST_FOR_EACH_ENTRY( surface, &window_surfaces, struct window_surface, entry )
surface->funcs->flush( surface );
done:
pthread_mutex_unlock( &surfaces_lock );
}
/*******************************************************************
* is_desktop_window
*
* Check if window is the desktop or the HWND_MESSAGE top parent.
*/
static BOOL is_desktop_window( HWND hwnd )
{
struct user_thread_info *thread_info = get_user_thread_info();
if (!hwnd) return FALSE;
if (hwnd == thread_info->top_window) return TRUE;
if (hwnd == thread_info->msg_window) return TRUE;
if (!HIWORD(hwnd) || HIWORD(hwnd) == 0xffff)
{
if (LOWORD(thread_info->top_window) == LOWORD(hwnd)) return TRUE;
if (LOWORD(thread_info->msg_window) == LOWORD(hwnd)) return TRUE;
}
return FALSE;
}
/***********************************************************************
* win_get_ptr
*
* Return a pointer to the WND structure if local to the process,
* or WND_OTHER_PROCESS if handle may be valid in other process.
* If ret value is a valid pointer, it must be released with WIN_ReleasePtr.
*/
WND *get_win_ptr( HWND hwnd )
{
WND *win;
if ((win = get_user_handle_ptr( hwnd, NTUSER_OBJ_WINDOW )) == WND_OTHER_PROCESS)
{
if (is_desktop_window( hwnd )) win = WND_DESKTOP;
}
return win;
}
/***********************************************************************
* is_current_thread_window
*
* Check whether a given window belongs to the current process (and return the full handle).
*/
HWND is_current_thread_window( HWND hwnd )
{
WND *win;
HWND ret = 0;
if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP)
return 0;
if (win->tid == GetCurrentThreadId()) ret = win->obj.handle;
release_win_ptr( win );
return ret;
}
/* see IsWindow */
BOOL is_window( HWND hwnd )
{
WND *win;
BOOL ret;
if (!(win = get_win_ptr( hwnd ))) return FALSE;
if (win == WND_DESKTOP) return TRUE;
if (win != WND_OTHER_PROCESS)
{
release_win_ptr( win );
return TRUE;
}
/* check other processes */
SERVER_START_REQ( get_window_info )
{
req->handle = wine_server_user_handle( hwnd );
ret = !wine_server_call_err( req );
}
SERVER_END_REQ;
return ret;
}
/* see GetWindowThreadProcessId */
static DWORD get_window_thread( HWND hwnd, DWORD *process )
{
WND *ptr;
DWORD tid = 0;
if (!(ptr = get_win_ptr( hwnd )))
{
SetLastError( ERROR_INVALID_WINDOW_HANDLE);
return 0;
}
if (ptr != WND_OTHER_PROCESS && ptr != WND_DESKTOP)
{
/* got a valid window */
tid = ptr->tid;
if (process) *process = GetCurrentProcessId();
release_win_ptr( ptr );
return tid;
}
/* check other processes */
SERVER_START_REQ( get_window_info )
{
req->handle = wine_server_user_handle( hwnd );
if (!wine_server_call_err( req ))
{
tid = (DWORD)reply->tid;
if (process) *process = (DWORD)reply->pid;
}
}
SERVER_END_REQ;
return tid;
}
/* see GetParent */
static HWND get_parent( HWND hwnd )
{
HWND retval = 0;
WND *win;
if (!(win = get_win_ptr( hwnd )))
{
SetLastError( ERROR_INVALID_WINDOW_HANDLE );
return 0;
}
if (win == WND_DESKTOP) return 0;
if (win == WND_OTHER_PROCESS)
{
LONG style = get_window_long( hwnd, GWL_STYLE );
if (style & (WS_POPUP | WS_CHILD))
{
SERVER_START_REQ( get_window_tree )
{
req->handle = wine_server_user_handle( hwnd );
if (!wine_server_call_err( req ))
{
if (style & WS_POPUP) retval = wine_server_ptr_handle( reply->owner );
else if (style & WS_CHILD) retval = wine_server_ptr_handle( reply->parent );
}
}
SERVER_END_REQ;
}
}
else
{
if (win->dwStyle & WS_POPUP) retval = win->owner;
else if (win->dwStyle & WS_CHILD) retval = win->parent;
release_win_ptr( win );
}
return retval;
}
/* see GetWindow */
static HWND get_window_relative( HWND hwnd, UINT rel )
{
HWND retval = 0;
if (rel == GW_OWNER) /* this one may be available locally */
{
WND *win = get_win_ptr( hwnd );
if (!win)
{
SetLastError( ERROR_INVALID_HANDLE );
return 0;
}
if (win == WND_DESKTOP) return 0;
if (win != WND_OTHER_PROCESS)
{
retval = win->owner;
release_win_ptr( win );
return retval;
}
/* else fall through to server call */
}
SERVER_START_REQ( get_window_tree )
{
req->handle = wine_server_user_handle( hwnd );
if (!wine_server_call_err( req ))
{
switch(rel)
{
case GW_HWNDFIRST:
retval = wine_server_ptr_handle( reply->first_sibling );
break;
case GW_HWNDLAST:
retval = wine_server_ptr_handle( reply->last_sibling );
break;
case GW_HWNDNEXT:
retval = wine_server_ptr_handle( reply->next_sibling );
break;
case GW_HWNDPREV:
retval = wine_server_ptr_handle( reply->prev_sibling );
break;
case GW_OWNER:
retval = wine_server_ptr_handle( reply->owner );
break;
case GW_CHILD:
retval = wine_server_ptr_handle( reply->first_child );
break;
}
}
}
SERVER_END_REQ;
return retval;
}
/*******************************************************************
* list_window_parents
*
* Build an array of all parents of a given window, starting with
* the immediate parent. The array must be freed with free().
*/
static HWND *list_window_parents( HWND hwnd )
{
WND *win;
HWND current, *list;
int i, pos = 0, size = 16, count;
if (!(list = malloc( size * sizeof(HWND) ))) return NULL;
current = hwnd;
for (;;)
{
if (!(win = get_win_ptr( current ))) goto empty;
if (win == WND_OTHER_PROCESS) break; /* need to do it the hard way */
if (win == WND_DESKTOP)
{
if (!pos) goto empty;
list[pos] = 0;
return list;
}
list[pos] = current = win->parent;
release_win_ptr( win );
if (!current) return list;
if (++pos == size - 1)
{
/* need to grow the list */
HWND *new_list = realloc( list, (size + 16) * sizeof(HWND) );
if (!new_list) goto empty;
list = new_list;
size += 16;
}
}
/* at least one parent belongs to another process, have to query the server */
for (;;)
{
count = 0;
SERVER_START_REQ( get_window_parents )
{
req->handle = wine_server_user_handle( hwnd );
wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
if (!wine_server_call( req )) count = reply->count;
}
SERVER_END_REQ;
if (!count) goto empty;
if (size > count)
{
/* start from the end since HWND is potentially larger than user_handle_t */
for (i = count - 1; i >= 0; i--)
list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
list[count] = 0;
return list;
}
free( list );
size = count + 1;
if (!(list = malloc( size * sizeof(HWND) ))) return NULL;
}
empty:
free( list );
return NULL;
}
/*****************************************************************
* NtUserGetAncestor (win32u.@)
*/
HWND WINAPI NtUserGetAncestor( HWND hwnd, UINT type )
{
HWND *list, ret = 0;
WND *win;
switch(type)
{
case GA_PARENT:
if (!(win = get_win_ptr( hwnd )))
{
SetLastError( ERROR_INVALID_WINDOW_HANDLE );
return 0;
}
if (win == WND_DESKTOP) return 0;
if (win != WND_OTHER_PROCESS)
{
ret = win->parent;
release_win_ptr( win );
}
else /* need to query the server */
{
SERVER_START_REQ( get_window_tree )
{
req->handle = wine_server_user_handle( hwnd );
if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->parent );
}
SERVER_END_REQ;
}
break;
case GA_ROOT:
if (!(list = list_window_parents( hwnd ))) return 0;
if (!list[0] || !list[1]) ret = get_full_window_handle( hwnd ); /* top-level window */
else
{
int count = 2;
while (list[count]) count++;
ret = list[count - 2]; /* get the one before the desktop */
}
free( list );
break;
case GA_ROOTOWNER:
if (is_desktop_window( hwnd )) return 0;
ret = get_full_window_handle( hwnd );
for (;;)
{
HWND parent = get_parent( ret );
if (!parent) break;
ret = parent;
}
break;
}
return ret;
}
/* see IsChild */
BOOL is_child( HWND parent, HWND child )
{
HWND *list;
int i;
BOOL ret = FALSE;
if (!(get_window_long( child, GWL_STYLE ) & WS_CHILD)) return FALSE;
if (!(list = list_window_parents( child ))) return FALSE;
parent = get_full_window_handle( parent );
for (i = 0; list[i]; i++)
{
if (list[i] == parent)
{
ret = list[i] && list[i+1];
break;
}
if (!(get_window_long( list[i], GWL_STYLE ) & WS_CHILD)) break;
}
free( list );
return ret;
}
/* see IsWindowVisible */
static BOOL is_window_visible( HWND hwnd )
{
HWND *list;
BOOL retval = TRUE;
int i;
if (!(get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE)) return FALSE;
if (!(list = list_window_parents( hwnd ))) return TRUE;
if (list[0])
{
for (i = 0; list[i+1]; i++)
if (!(get_window_long( list[i], GWL_STYLE ) & WS_VISIBLE)) break;
retval = !list[i+1] && (list[i] == get_desktop_window()); /* top message window isn't visible */
}
free( list );
return retval;
}
/***********************************************************************
* is_window_drawable
*
* hwnd is drawable when it is visible, all parents are not
* minimized, and it is itself not minimized unless we are
* trying to draw its default class icon.
*/
static BOOL is_window_drawable( HWND hwnd, BOOL icon )
{
HWND *list;
BOOL retval = TRUE;
int i;
LONG style = get_window_long( hwnd, GWL_STYLE );
if (!(style & WS_VISIBLE)) return FALSE;
if ((style & WS_MINIMIZE) && icon && get_class_long_ptr( hwnd, GCLP_HICON, FALSE )) return FALSE;
if (!(list = list_window_parents( hwnd ))) return TRUE;
if (list[0])
{
for (i = 0; list[i+1]; i++)
if ((get_window_long( list[i], GWL_STYLE ) & (WS_VISIBLE|WS_MINIMIZE)) != WS_VISIBLE)
break;
retval = !list[i+1] && (list[i] == get_desktop_window()); /* top message window isn't visible */
}
free( list );
return retval;
}
/* see IsWindowUnicode */
static BOOL is_window_unicode( HWND hwnd )
{
WND *win;
BOOL ret = FALSE;
if (!(win = get_win_ptr(hwnd))) return FALSE;
if (win == WND_DESKTOP) return TRUE;
if (win != WND_OTHER_PROCESS)
{
ret = (win->flags & WIN_ISUNICODE) != 0;
release_win_ptr( win );
}
else
{
SERVER_START_REQ( get_window_info )
{
req->handle = wine_server_user_handle( hwnd );
if (!wine_server_call_err( req )) ret = reply->is_unicode;
}
SERVER_END_REQ;
}
return ret;
}
/* see GetWindowDpiAwarenessContext */
static DPI_AWARENESS_CONTEXT get_window_dpi_awareness_context( HWND hwnd )
{
DPI_AWARENESS_CONTEXT ret = 0;
WND *win;
if (!(win = get_win_ptr( hwnd )))
{
SetLastError( ERROR_INVALID_WINDOW_HANDLE );
return 0;
}
if (win == WND_DESKTOP) return DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE;
if (win != WND_OTHER_PROCESS)
{
ret = ULongToHandle( win->dpi_awareness | 0x10 );
release_win_ptr( win );
}
else
{
SERVER_START_REQ( get_window_info )
{
req->handle = wine_server_user_handle( hwnd );
if (!wine_server_call_err( req )) ret = ULongToHandle( reply->awareness | 0x10 );
}
SERVER_END_REQ;
}
return ret;
}
/* see GetDpiForWindow */
static UINT get_dpi_for_window( HWND hwnd )
{
WND *win;
UINT ret = 0;
if (!(win = get_win_ptr( hwnd )))
{
SetLastError( ERROR_INVALID_WINDOW_HANDLE );
return 0;
}
if (win == WND_DESKTOP)
{
POINT pt = { 0, 0 };
return get_monitor_dpi( monitor_from_point( pt, MONITOR_DEFAULTTOPRIMARY, 0 ));
}
if (win != WND_OTHER_PROCESS)
{
ret = win->dpi;
if (!ret) ret = get_win_monitor_dpi( hwnd );
release_win_ptr( win );
}
else
{
SERVER_START_REQ( get_window_info )
{
req->handle = wine_server_user_handle( hwnd );
if (!wine_server_call_err( req )) ret = reply->dpi;
}
SERVER_END_REQ;
}
return ret;
}
static LONG_PTR get_win_data( const void *ptr, UINT size )
{
if (size == sizeof(WORD))
{
WORD ret;
memcpy( &ret, ptr, sizeof(ret) );
return ret;
}
else if (size == sizeof(DWORD))
{
DWORD ret;
memcpy( &ret, ptr, sizeof(ret) );
return ret;
}
else
{
LONG_PTR ret;
memcpy( &ret, ptr, sizeof(ret) );
return ret;
}
}
static BOOL is_iconic( HWND hwnd )
{
return (get_window_long( hwnd, GWL_STYLE ) & WS_MINIMIZE) != 0;
}
static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ansi )
{
LONG_PTR retval = 0;
WND *win;
if (offset == GWLP_HWNDPARENT)
{
HWND parent = NtUserGetAncestor( hwnd, GA_PARENT );
if (parent == get_desktop_window())
parent = get_window_relative( hwnd, GW_OWNER );
return (ULONG_PTR)parent;
}
if (!(win = get_win_ptr( hwnd )))
{
SetLastError( ERROR_INVALID_WINDOW_HANDLE );
return 0;
}
if (win == WND_DESKTOP)
{
switch (offset)
{
case GWL_STYLE:
retval = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; /* message parent is not visible */
if (get_full_window_handle( hwnd ) == get_desktop_window())
retval |= WS_VISIBLE;
return retval;
case GWL_EXSTYLE:
case GWLP_USERDATA:
case GWLP_ID:
case GWLP_HINSTANCE:
return 0;
case GWLP_WNDPROC:
SetLastError( ERROR_ACCESS_DENIED );
return 0;
}
SetLastError( ERROR_INVALID_INDEX );
return 0;
}
if (win == WND_OTHER_PROCESS)
{
if (offset == GWLP_WNDPROC)
{
SetLastError( ERROR_ACCESS_DENIED );
return 0;
}
SERVER_START_REQ( set_window_info )
{
req->handle = wine_server_user_handle( hwnd );
req->flags = 0; /* don't set anything, just retrieve */
req->extra_offset = (offset >= 0) ? offset : -1;
req->extra_size = (offset >= 0) ? size : 0;
if (!wine_server_call_err( req ))
{
switch(offset)
{
case GWL_STYLE: retval = reply->old_style; break;
case GWL_EXSTYLE: retval = reply->old_ex_style; break;
case GWLP_ID: retval = reply->old_id; break;
case GWLP_HINSTANCE: retval = (ULONG_PTR)wine_server_get_ptr( reply->old_instance ); break;
case GWLP_USERDATA: retval = reply->old_user_data; break;
default:
if (offset >= 0) retval = get_win_data( &reply->old_extra_value, size );
else SetLastError( ERROR_INVALID_INDEX );
break;
}
}
}
SERVER_END_REQ;
return retval;
}
/* now we have a valid win */
if (offset >= 0)
{
if (offset > (int)(win->cbWndExtra - size))
{
WARN("Invalid offset %d\n", offset );
release_win_ptr( win );
SetLastError( ERROR_INVALID_INDEX );
return 0;
}
retval = get_win_data( (char *)win->wExtra + offset, size );
/* Special case for dialog window procedure */
if ((offset == DWLP_DLGPROC) && (size == sizeof(LONG_PTR)) && win->dlgInfo)
retval = (LONG_PTR)get_winproc( (WNDPROC)retval, ansi );
release_win_ptr( win );
return retval;
}
switch(offset)
{
case GWLP_USERDATA: retval = win->userdata; break;
case GWL_STYLE: retval = win->dwStyle; break;
case GWL_EXSTYLE: retval = win->dwExStyle; break;
case GWLP_ID: retval = win->wIDmenu; break;
case GWLP_HINSTANCE: retval = (ULONG_PTR)win->hInstance; break;
case GWLP_WNDPROC:
/* This looks like a hack only for the edit control (see tests). This makes these controls
* more tolerant to A/W mismatches. The lack of W->A->W conversion for such a mismatch suggests
* that the hack is in GetWindowLongPtr[AW], not in winprocs.
*/
if (win->winproc == BUILTIN_WINPROC(WINPROC_EDIT) && (!!ansi != !(win->flags & WIN_ISUNICODE)))
retval = (ULONG_PTR)win->winproc;
else
retval = (ULONG_PTR)get_winproc( win->winproc, ansi );
break;
default:
WARN("Unknown offset %d\n", offset );
SetLastError( ERROR_INVALID_INDEX );
break;
}
release_win_ptr( win );
return retval;
}
/* see GetWindowLongW */
DWORD get_window_long( HWND hwnd, INT offset )
{
return get_window_long_size( hwnd, offset, sizeof(LONG), FALSE );
}
/* see GetWindowLongPtr */
static ULONG_PTR get_window_long_ptr( HWND hwnd, INT offset, BOOL ansi )
{
return get_window_long_size( hwnd, offset, sizeof(LONG_PTR), ansi );
}
/* see GetWindowWord */
static WORD get_window_word( HWND hwnd, INT offset )
{
switch(offset)
{
case GWLP_ID:
case GWLP_HINSTANCE:
case GWLP_HWNDPARENT:
break;
default:
if (offset < 0)
{
WARN("Invalid offset %d\n", offset );
SetLastError( ERROR_INVALID_INDEX );
return 0;
}
break;
}
return get_window_long_size( hwnd, offset, sizeof(WORD), TRUE );
}
/***********************************************************************
* NtUserGetProp (win32u.@)
*
* NOTE Native allows only ATOMs as the second argument. We allow strings
* to save extra server call in GetPropW.
*/
HANDLE WINAPI NtUserGetProp( HWND hwnd, const WCHAR *str )
{
ULONG_PTR ret = 0;
SERVER_START_REQ( get_window_property )
{
req->window = wine_server_user_handle( hwnd );
if (IS_INTRESOURCE(str)) req->atom = LOWORD(str);
else wine_server_add_data( req, str, lstrlenW(str) * sizeof(WCHAR) );
if (!wine_server_call_err( req )) ret = reply->data;
}
SERVER_END_REQ;
return (HANDLE)ret;
}
/*****************************************************************************
* NtUserSetProp (win32u.@)
*
* NOTE Native allows only ATOMs as the second argument. We allow strings
* to save extra server call in SetPropW.
*/
BOOL WINAPI NtUserSetProp( HWND hwnd, const WCHAR *str, HANDLE handle )
{
BOOL ret;
SERVER_START_REQ( set_window_property )
{
req->window = wine_server_user_handle( hwnd );
req->data = (ULONG_PTR)handle;
if (IS_INTRESOURCE(str)) req->atom = LOWORD(str);
else wine_server_add_data( req, str, lstrlenW(str) * sizeof(WCHAR) );
ret = !wine_server_call_err( req );
}
SERVER_END_REQ;
return ret;
}
/***********************************************************************
* NtUserRemoveProp (win32u.@)
*
* NOTE Native allows only ATOMs as the second argument. We allow strings
* to save extra server call in RemovePropW.
*/
HANDLE WINAPI NtUserRemoveProp( HWND hwnd, const WCHAR *str )
{
ULONG_PTR ret = 0;
SERVER_START_REQ( remove_window_property )
{
req->window = wine_server_user_handle( hwnd );
if (IS_INTRESOURCE(str)) req->atom = LOWORD(str);
else wine_server_add_data( req, str, lstrlenW(str) * sizeof(WCHAR) );
if (!wine_server_call_err( req )) ret = reply->data;
}
SERVER_END_REQ;
return (HANDLE)ret;
}
static void mirror_rect( const RECT *window_rect, RECT *rect )
{
int width = window_rect->right - window_rect->left;
int tmp = rect->left;
rect->left = width - rect->right;
rect->right = width - tmp;
}
/***********************************************************************
* get_window_rects
*
* Get the window and client rectangles.
*/
static BOOL get_window_rects( HWND hwnd, enum coords_relative relative, RECT *window_rect,
RECT *client_rect, UINT dpi )
{
WND *win = get_win_ptr( hwnd );
BOOL ret = TRUE;
if (!win)
{
SetLastError( ERROR_INVALID_WINDOW_HANDLE );
return FALSE;
}
if (win == WND_DESKTOP)
{
RECT rect;
rect.left = rect.top = 0;
if (hwnd == get_hwnd_message_parent())
{
rect.right = 100;
rect.bottom = 100;
rect = map_dpi_rect( rect, get_dpi_for_window( hwnd ), dpi );
}
else
{
rect = get_primary_monitor_rect( dpi );
}
if (window_rect) *window_rect = rect;
if (client_rect) *client_rect = rect;
return TRUE;
}
if (win != WND_OTHER_PROCESS)
{
UINT window_dpi = get_dpi_for_window( hwnd );
RECT window = win->window_rect;
RECT client = win->client_rect;
switch (relative)
{
case COORDS_CLIENT:
OffsetRect( &window, -win->client_rect.left, -win->client_rect.top );
OffsetRect( &client, -win->client_rect.left, -win->client_rect.top );
if (win->dwExStyle & WS_EX_LAYOUTRTL)
mirror_rect( &win->client_rect, &window );
break;
case COORDS_WINDOW:
OffsetRect( &window, -win->window_rect.left, -win->window_rect.top );
OffsetRect( &client, -win->window_rect.left, -win->window_rect.top );
if (win->dwExStyle & WS_EX_LAYOUTRTL)
mirror_rect( &win->window_rect, &client );
break;
case COORDS_PARENT:
if (win->parent)
{
WND *parent = get_win_ptr( win->parent );
if (parent == WND_DESKTOP) break;
if (!parent || parent == WND_OTHER_PROCESS)
{
release_win_ptr( win );
goto other_process;
}
if (parent->flags & WIN_CHILDREN_MOVED)
{
release_win_ptr( parent );
release_win_ptr( win );
goto other_process;
}
if (parent->dwExStyle & WS_EX_LAYOUTRTL)
{
mirror_rect( &parent->client_rect, &window );
mirror_rect( &parent->client_rect, &client );
}
release_win_ptr( parent );
}
break;
case COORDS_SCREEN:
while (win->parent)
{
WND *parent = get_win_ptr( win->parent );
if (parent == WND_DESKTOP) break;
if (!parent || parent == WND_OTHER_PROCESS)
{
release_win_ptr( win );
goto other_process;
}
release_win_ptr( win );
if (parent->flags & WIN_CHILDREN_MOVED)
{
release_win_ptr( parent );
goto other_process;
}
win = parent;
if (win->parent)
{
offset_rect( &window, win->client_rect.left, win->client_rect.top );
offset_rect( &client, win->client_rect.left, win->client_rect.top );
}
}
break;
}
if (window_rect) *window_rect = map_dpi_rect( window, window_dpi, dpi );
if (client_rect) *client_rect = map_dpi_rect( client, window_dpi, dpi );
release_win_ptr( win );
return TRUE;
}
other_process:
SERVER_START_REQ( get_window_rectangles )
{
req->handle = wine_server_user_handle( hwnd );
req->relative = relative;
req->dpi = dpi;
if ((ret = !wine_server_call_err( req )))
{
if (window_rect)
{
window_rect->left = reply->window.left;
window_rect->top = reply->window.top;
window_rect->right = reply->window.right;
window_rect->bottom = reply->window.bottom;
}
if (client_rect)
{
client_rect->left = reply->client.left;
client_rect->top = reply->client.top;
client_rect->right = reply->client.right;
client_rect->bottom = reply->client.bottom;
}
}
}
SERVER_END_REQ;
return ret;
}
/* see GetWindowRect */
BOOL get_window_rect( HWND hwnd, RECT *rect )
{
return get_window_rects( hwnd, COORDS_SCREEN, rect, NULL, get_thread_dpi() );
}
/* see GetClientRect */
static BOOL get_client_rect( HWND hwnd, RECT *rect )
{
return get_window_rects( hwnd, COORDS_CLIENT, NULL, rect, get_thread_dpi() );
}
/* see GetWindowInfo */
static BOOL get_window_info( HWND hwnd, WINDOWINFO *info )
{
if (!info || !get_window_rects( hwnd, COORDS_SCREEN, &info->rcWindow,
&info->rcClient, get_thread_dpi() ))
return FALSE;
info->dwStyle = get_window_long( hwnd, GWL_STYLE );
info->dwExStyle = get_window_long( hwnd, GWL_EXSTYLE );
info->dwWindowStatus = get_active_window() == hwnd ? WS_ACTIVECAPTION : 0;
info->cxWindowBorders = info->rcClient.left - info->rcWindow.left;
info->cyWindowBorders = info->rcWindow.bottom - info->rcClient.bottom;
info->atomWindowType = get_class_long( hwnd, GCW_ATOM, FALSE );
info->wCreatorVersion = 0x0400;
return TRUE;
}
/*******************************************************************
* NtUserGetWindowRgnEx (win32u.@)
*/
int WINAPI NtUserGetWindowRgnEx( HWND hwnd, HRGN hrgn, UINT unk )
{
NTSTATUS status;
HRGN win_rgn = 0;
RGNDATA *data;
size_t size = 256;
int ret = ERROR;
do
{
if (!(data = malloc( sizeof(*data) + size - 1 )))
{
SetLastError( ERROR_OUTOFMEMORY );
return ERROR;
}
SERVER_START_REQ( get_window_region )
{
req->window = wine_server_user_handle( hwnd );
wine_server_set_reply( req, data->Buffer, size );
if (!(status = wine_server_call( req )))
{
size_t reply_size = wine_server_reply_size( reply );
if (reply_size)
{
data->rdh.dwSize = sizeof(data->rdh);
data->rdh.iType = RDH_RECTANGLES;
data->rdh.nCount = reply_size / sizeof(RECT);
data->rdh.nRgnSize = reply_size;
win_rgn = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
}
}
else size = reply->total_size;
}
SERVER_END_REQ;
free( data );
} while (status == STATUS_BUFFER_OVERFLOW);
if (set_ntstatus( status ) && win_rgn)
{
ret = NtGdiCombineRgn( hrgn, win_rgn, 0, RGN_COPY );
NtGdiDeleteObjectApp( win_rgn );
}
return ret;
}
/*****************************************************************************
* NtUserGetLayeredWindowAttributes (win32u.@)
*/
BOOL WINAPI NtUserGetLayeredWindowAttributes( HWND hwnd, COLORREF *key, BYTE *alpha, DWORD *flags )
{
BOOL ret;
SERVER_START_REQ( get_window_layered_info )
{
req->handle = wine_server_user_handle( hwnd );
if ((ret = !wine_server_call_err( req )))
{
if (key) *key = reply->color_key;
if (alpha) *alpha = reply->alpha;
if (flags) *flags = reply->flags;
}
}
SERVER_END_REQ;
return ret;
}
/*****************************************************************************
* NtUserBuildHwndList (win32u.@)
*/
NTSTATUS WINAPI NtUserBuildHwndList( HDESK desktop, ULONG unk2, ULONG unk3, ULONG unk4,
ULONG thread_id, ULONG count, HWND *buffer, ULONG *size )
{
user_handle_t *list = (user_handle_t *)buffer;
int i;
NTSTATUS status;
SERVER_START_REQ( get_window_children )
{
req->desktop = wine_server_obj_handle( desktop );
req->tid = thread_id;
if (count) wine_server_set_reply( req, list, (count - 1) * sizeof(user_handle_t) );
status = wine_server_call( req );
if (status && status != STATUS_BUFFER_TOO_SMALL) return status;
*size = reply->count + 1;
}
SERVER_END_REQ;
if (*size > count) return STATUS_BUFFER_TOO_SMALL;
/* start from the end since HWND is potentially larger than user_handle_t */
for (i = *size - 2; i >= 0; i--)
buffer[i] = wine_server_ptr_handle( list[i] );
buffer[*size - 1] = HWND_BOTTOM;
return STATUS_SUCCESS;
}
/* Retrieve the window text from the server. */
static data_size_t get_server_window_text( HWND hwnd, WCHAR *text, data_size_t count )
{
data_size_t len = 0, needed = 0;
SERVER_START_REQ( get_window_text )
{
req->handle = wine_server_user_handle( hwnd );
if (count) wine_server_set_reply( req, text, (count - 1) * sizeof(WCHAR) );
if (!wine_server_call_err( req ))
{
needed = reply->length;
len = wine_server_reply_size(reply);
}
}
SERVER_END_REQ;
if (text) text[len / sizeof(WCHAR)] = 0;
return needed;
}
/*******************************************************************
* NtUserInternalGetWindowText (win32u.@)
*/
INT WINAPI NtUserInternalGetWindowText( HWND hwnd, WCHAR *text, INT count )
{
WND *win;
if (count <= 0) return 0;
if (!(win = get_win_ptr( hwnd ))) return 0;
if (win == WND_DESKTOP) text[0] = 0;
else if (win != WND_OTHER_PROCESS)
{
if (win->text) lstrcpynW( text, win->text, count );
else text[0] = 0;
release_win_ptr( win );
}
else
{
get_server_window_text( hwnd, text, count );
}
return lstrlenW(text);
}
/*******************************************************************
* NtUserFlashWindowEx (win32u.@)
*/
BOOL WINAPI NtUserFlashWindowEx( FLASHWINFO *info )
{
WND *win;
TRACE( "%p\n", info );
if (!info)
{
SetLastError( ERROR_NOACCESS );
return FALSE;
}
if (!info->hwnd || info->cbSize != sizeof(FLASHWINFO) || !is_window( info->hwnd ))
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
FIXME( "%p - semi-stub\n", info );
if (is_iconic( info->hwnd ))
{
user_callbacks->pRedrawWindow( info->hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_FRAME );
win = get_win_ptr( info->hwnd );
if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
if (info->dwFlags && !(win->flags & WIN_NCACTIVATED))
{
win->flags |= WIN_NCACTIVATED;
}
else
{
win->flags &= ~WIN_NCACTIVATED;
}
release_win_ptr( win );
user_driver->pFlashWindowEx( info );
return TRUE;
}
else
{
WPARAM wparam;
HWND hwnd = info->hwnd;
win = get_win_ptr( hwnd );
if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
hwnd = win->obj.handle; /* make it a full handle */
if (info->dwFlags) wparam = !(win->flags & WIN_NCACTIVATED);
else wparam = (hwnd == NtUserGetForegroundWindow());
release_win_ptr( win );
send_message( hwnd, WM_NCACTIVATE, wparam, 0 );
user_driver->pFlashWindowEx( info );
return wparam;
}
}
/*****************************************************************************
* NtUserCallHwnd (win32u.@)
*/
ULONG_PTR WINAPI NtUserCallHwnd( HWND hwnd, DWORD code )
{
switch (code)
{
case NtUserGetDpiForWindow:
return get_dpi_for_window( hwnd );
case NtUserGetParent:
return HandleToUlong( get_parent( hwnd ));
case NtUserGetWindowDpiAwarenessContext:
return (ULONG_PTR)get_window_dpi_awareness_context( hwnd );
case NtUserGetWindowTextLength:
return get_server_window_text( hwnd, NULL, 0 );
case NtUserIsWindow:
return is_window( hwnd );
case NtUserIsWindowUnicode:
return is_window_unicode( hwnd );
case NtUserIsWindowVisible:
return is_window_visible( hwnd );
/* temporary exports */
case NtUserCreateDesktopWindow:
return user_driver->pCreateDesktopWindow( hwnd );
default:
FIXME( "invalid code %u\n", code );
return 0;
}
}
/*****************************************************************************
* NtUserCallHwndParam (win32u.@)
*/
ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code )
{
switch (code)
{
case NtUserGetClassLongA:
return get_class_long( hwnd, param, TRUE );
case NtUserGetClassLongW:
return get_class_long( hwnd, param, FALSE );
case NtUserGetClassLongPtrA:
return get_class_long_ptr( hwnd, param, TRUE );
case NtUserGetClassLongPtrW:
return get_class_long_ptr( hwnd, param, FALSE );
case NtUserGetClassWord:
return get_class_word( hwnd, param );
case NtUserGetClientRect:
return get_client_rect( hwnd, (RECT *)param );
case NtUserGetWindowInfo:
return get_window_info( hwnd, (WINDOWINFO *)param );
case NtUserGetWindowLongA:
return get_window_long_size( hwnd, param, sizeof(LONG), TRUE );
case NtUserGetWindowLongW:
return get_window_long( hwnd, param );
case NtUserGetWindowLongPtrA:
return get_window_long_ptr( hwnd, param, TRUE );
case NtUserGetWindowLongPtrW:
return get_window_long_ptr( hwnd, param, FALSE );
case NtUserGetWindowRect:
return get_window_rect( hwnd, (RECT *)param );
case NtUserGetWindowRelative:
return HandleToUlong( get_window_relative( hwnd, param ));
case NtUserGetWindowThread:
return get_window_thread( hwnd, (DWORD *)param );
case NtUserGetWindowWord:
return get_window_word( hwnd, param );
case NtUserIsChild:
return is_child( hwnd, UlongToHandle(param) );
/* temporary exports */
case NtUserIsWindowDrawable:
return is_window_drawable( hwnd, param );
default:
FIXME( "invalid code %u\n", code );
return 0;
}
}