Sweden-Number/dlls/winemac.drv/window.c

756 lines
24 KiB
C
Raw Normal View History

/*
* MACDRV windowing driver
*
* Copyright 1993, 1994, 1995, 1996, 2001 Alexandre Julliard
* Copyright 1993 David Metcalfe
* Copyright 1995, 1996 Alex Korobka
* Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
*
* 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
*/
#include "config.h"
#include "macdrv.h"
#include "winuser.h"
#include "wine/unicode.h"
#include "wine/server.h"
WINE_DEFAULT_DEBUG_CHANNEL(macdrv);
static CRITICAL_SECTION win_data_section;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
0, 0, &win_data_section,
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": win_data_section") }
};
static CRITICAL_SECTION win_data_section = { &critsect_debug, -1, 0, 0, 0, 0 };
static CFMutableDictionaryRef win_datas;
/***********************************************************************
* get_cocoa_window_features
*/
static void get_cocoa_window_features(struct macdrv_win_data *data,
DWORD style, DWORD ex_style,
struct macdrv_window_features* wf)
{
memset(wf, 0, sizeof(*wf));
if (IsRectEmpty(&data->window_rect)) return;
if ((style & WS_CAPTION) == WS_CAPTION && !(ex_style & WS_EX_LAYERED))
{
wf->shadow = 1;
wf->title_bar = 1;
if (style & WS_SYSMENU) wf->close_button = 1;
if (style & WS_MINIMIZEBOX) wf->minimize_button = 1;
if (style & WS_MAXIMIZEBOX) wf->resizable = 1;
if (ex_style & WS_EX_TOOLWINDOW) wf->utility = 1;
}
if (ex_style & WS_EX_DLGMODALFRAME) wf->shadow = 1;
else if (style & WS_THICKFRAME)
{
wf->shadow = 1;
wf->resizable = 1;
}
else if ((style & (WS_DLGFRAME|WS_BORDER)) == WS_DLGFRAME) wf->shadow = 1;
}
/*******************************************************************
* can_activate_window
*
* Check if we can activate the specified window.
*/
static inline BOOL can_activate_window(HWND hwnd)
{
LONG style = GetWindowLongW(hwnd, GWL_STYLE);
RECT rect;
if (!(style & WS_VISIBLE)) return FALSE;
if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
if (style & WS_MINIMIZE) return FALSE;
if (GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_NOACTIVATE) return FALSE;
if (hwnd == GetDesktopWindow()) return FALSE;
if (GetWindowRect(hwnd, &rect) && IsRectEmpty(&rect)) return FALSE;
return !(style & WS_DISABLED);
}
/***********************************************************************
* get_cocoa_window_state
*/
static void get_cocoa_window_state(struct macdrv_win_data *data,
DWORD style, DWORD ex_style,
struct macdrv_window_state* state)
{
memset(state, 0, sizeof(*state));
state->disabled = (style & WS_DISABLED) != 0;
state->no_activate = !can_activate_window(data->hwnd);
state->floating = (ex_style & WS_EX_TOPMOST) != 0;
state->excluded_by_expose = state->excluded_by_cycle =
!(ex_style & WS_EX_APPWINDOW) &&
(GetWindow(data->hwnd, GW_OWNER) || (ex_style & (WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE)));
}
/***********************************************************************
* get_mac_rect_offset
*
* Helper for macdrv_window_to_mac_rect and macdrv_mac_to_window_rect.
*/
static void get_mac_rect_offset(struct macdrv_win_data *data, DWORD style, RECT *rect)
{
DWORD ex_style, style_mask = 0, ex_style_mask = 0;
struct macdrv_window_features wf;
rect->top = rect->bottom = rect->left = rect->right = 0;
ex_style = GetWindowLongW(data->hwnd, GWL_EXSTYLE);
get_cocoa_window_features(data, style, ex_style, &wf);
if (wf.title_bar) style_mask |= WS_CAPTION;
if (wf.shadow)
{
style_mask |= WS_DLGFRAME | WS_THICKFRAME;
ex_style_mask |= WS_EX_DLGMODALFRAME;
}
AdjustWindowRectEx(rect, style & style_mask, FALSE, ex_style & ex_style_mask);
TRACE("%p/%p style %08x ex_style %08x -> %s\n", data->hwnd, data->cocoa_window,
style, ex_style, wine_dbgstr_rect(rect));
}
/***********************************************************************
* show_window
*/
static void show_window(struct macdrv_win_data *data)
{
TRACE("win %p/%p\n", data->hwnd, data->cocoa_window);
data->on_screen = macdrv_order_cocoa_window(data->cocoa_window, NULL, NULL);
}
/***********************************************************************
* hide_window
*/
static void hide_window(struct macdrv_win_data *data)
{
TRACE("win %p/%p\n", data->hwnd, data->cocoa_window);
macdrv_hide_cocoa_window(data->cocoa_window);
data->on_screen = FALSE;
}
/***********************************************************************
* macdrv_window_to_mac_rect
*
* Convert a rect from client to Mac window coordinates
*/
static void macdrv_window_to_mac_rect(struct macdrv_win_data *data, DWORD style, RECT *rect)
{
RECT rc;
if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return;
if (IsRectEmpty(rect)) return;
get_mac_rect_offset(data, style, &rc);
rect->left -= rc.left;
rect->right -= rc.right;
rect->top -= rc.top;
rect->bottom -= rc.bottom;
if (rect->top >= rect->bottom) rect->bottom = rect->top + 1;
if (rect->left >= rect->right) rect->right = rect->left + 1;
}
/***********************************************************************
* macdrv_mac_to_window_rect
*
* Opposite of macdrv_window_to_mac_rect
*/
static void macdrv_mac_to_window_rect(struct macdrv_win_data *data, RECT *rect)
{
RECT rc;
DWORD style = GetWindowLongW(data->hwnd, GWL_STYLE);
if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return;
if (IsRectEmpty(rect)) return;
get_mac_rect_offset(data, style, &rc);
rect->left += rc.left;
rect->right += rc.right;
rect->top += rc.top;
rect->bottom += rc.bottom;
if (rect->top >= rect->bottom) rect->bottom = rect->top + 1;
if (rect->left >= rect->right) rect->right = rect->left + 1;
}
/***********************************************************************
* constrain_window_frame
*
* Alter a window frame rectangle to fit within a) Cocoa's documented
* limits, and b) sane sizes, like twice the desktop rect.
*/
static void constrain_window_frame(CGRect* frame)
{
CGRect desktop_rect = macdrv_get_desktop_rect();
int max_width, max_height;
max_width = min(32000, 2 * CGRectGetWidth(desktop_rect));
max_height = min(32000, 2 * CGRectGetHeight(desktop_rect));
if (frame->origin.x < -16000) frame->origin.x = -16000;
if (frame->origin.y < -16000) frame->origin.y = -16000;
if (frame->origin.x > 16000) frame->origin.x = 16000;
if (frame->origin.y > 16000) frame->origin.y = 16000;
if (frame->size.width > max_width) frame->size.width = max_width;
if (frame->size.height > max_height) frame->size.height = max_height;
}
/***********************************************************************
* alloc_win_data
*/
static struct macdrv_win_data *alloc_win_data(HWND hwnd)
{
struct macdrv_win_data *data;
if ((data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data))))
{
data->hwnd = hwnd;
EnterCriticalSection(&win_data_section);
if (!win_datas)
win_datas = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
CFDictionarySetValue(win_datas, hwnd, data);
}
return data;
}
/***********************************************************************
* get_win_data
*
* Lock and return the data structure associated with a window.
*/
static struct macdrv_win_data *get_win_data(HWND hwnd)
{
struct macdrv_win_data *data;
if (!hwnd) return NULL;
EnterCriticalSection(&win_data_section);
if (win_datas && (data = (struct macdrv_win_data*)CFDictionaryGetValue(win_datas, hwnd)))
return data;
LeaveCriticalSection(&win_data_section);
return NULL;
}
/***********************************************************************
* release_win_data
*
* Release the data returned by get_win_data.
*/
static void release_win_data(struct macdrv_win_data *data)
{
if (data) LeaveCriticalSection(&win_data_section);
}
/***********************************************************************
* macdrv_get_cocoa_window
*
* Return the Mac window associated with the full area of a window
*/
static macdrv_window macdrv_get_cocoa_window(HWND hwnd)
{
struct macdrv_win_data *data = get_win_data(hwnd);
macdrv_window ret = data ? data->cocoa_window : NULL;
release_win_data(data);
return ret;
}
/***********************************************************************
* set_cocoa_window_properties
*
* Set the window properties for a Cocoa window based on its Windows
* properties.
*/
static void set_cocoa_window_properties(struct macdrv_win_data *data)
{
DWORD style, ex_style;
HWND owner;
macdrv_window owner_win;
struct macdrv_window_features wf;
struct macdrv_window_state state;
style = GetWindowLongW(data->hwnd, GWL_STYLE);
ex_style = GetWindowLongW(data->hwnd, GWL_EXSTYLE);
owner = GetWindow(data->hwnd, GW_OWNER);
owner_win = macdrv_get_cocoa_window(owner);
macdrv_set_cocoa_parent_window(data->cocoa_window, owner_win);
get_cocoa_window_features(data, style, ex_style, &wf);
macdrv_set_cocoa_window_features(data->cocoa_window, &wf);
get_cocoa_window_state(data, style, ex_style, &state);
macdrv_set_cocoa_window_state(data->cocoa_window, &state);
}
/**********************************************************************
* create_cocoa_window
*
* Create the whole Mac window for a given window
*/
static void create_cocoa_window(struct macdrv_win_data *data)
{
WCHAR text[1024];
struct macdrv_window_features wf;
CGRect frame;
DWORD style, ex_style;
style = GetWindowLongW(data->hwnd, GWL_STYLE);
ex_style = GetWindowLongW(data->hwnd, GWL_EXSTYLE);
data->whole_rect = data->window_rect;
macdrv_window_to_mac_rect(data, style, &data->whole_rect);
memset(&wf, 0, sizeof(wf));
get_cocoa_window_features(data, style, ex_style, &wf);
frame = cgrect_from_rect(data->whole_rect);
constrain_window_frame(&frame);
TRACE("creating %p window %s whole %s client %s\n", data->hwnd, wine_dbgstr_rect(&data->window_rect),
wine_dbgstr_rect(&data->whole_rect), wine_dbgstr_rect(&data->client_rect));
data->cocoa_window = macdrv_create_cocoa_window(&wf, frame);
if (!data->cocoa_window) return;
set_cocoa_window_properties(data);
/* set the window text */
if (!InternalGetWindowText(data->hwnd, text, sizeof(text)/sizeof(WCHAR))) text[0] = 0;
macdrv_set_cocoa_window_title(data->cocoa_window, text, strlenW(text));
}
/**********************************************************************
* destroy_cocoa_window
*
* Destroy the whole Mac window for a given window.
*/
static void destroy_cocoa_window(struct macdrv_win_data *data)
{
if (!data->cocoa_window) return;
TRACE("win %p Cocoa win %p\n", data->hwnd, data->cocoa_window);
macdrv_destroy_cocoa_window(data->cocoa_window);
data->cocoa_window = 0;
data->on_screen = FALSE;
}
/***********************************************************************
* macdrv_create_win_data
*
* Create a Mac data window structure for an existing window.
*/
static struct macdrv_win_data *macdrv_create_win_data(HWND hwnd, const RECT *window_rect,
const RECT *client_rect)
{
struct macdrv_win_data *data;
HWND parent;
if (GetWindowThreadProcessId(hwnd, NULL) != GetCurrentThreadId()) return NULL;
if (!(parent = GetAncestor(hwnd, GA_PARENT))) /* desktop */
return NULL;
/* don't create win data for HWND_MESSAGE windows */
if (parent != GetDesktopWindow() && !GetAncestor(parent, GA_PARENT)) return NULL;
if (!(data = alloc_win_data(hwnd))) return NULL;
data->whole_rect = data->window_rect = *window_rect;
data->client_rect = *client_rect;
if (parent == GetDesktopWindow())
{
create_cocoa_window(data);
TRACE("win %p/%p window %s whole %s client %s\n",
hwnd, data->cocoa_window, wine_dbgstr_rect(&data->window_rect),
wine_dbgstr_rect(&data->whole_rect), wine_dbgstr_rect(&data->client_rect));
}
return data;
}
/***********************************************************************
* sync_window_position
*
* Synchronize the Mac window position with the Windows one
*/
static void sync_window_position(struct macdrv_win_data *data, UINT swp_flags)
{
CGRect frame = cgrect_from_rect(data->whole_rect);
constrain_window_frame(&frame);
data->on_screen = macdrv_set_cocoa_window_frame(data->cocoa_window, &frame);
TRACE("win %p/%p pos %s\n", data->hwnd, data->cocoa_window,
wine_dbgstr_rect(&data->whole_rect));
if (data->on_screen && (!(swp_flags & SWP_NOZORDER) || (swp_flags & SWP_SHOWWINDOW)))
{
HWND next = NULL;
macdrv_window prev_window = NULL;
macdrv_window next_window = NULL;
/* find window that this one must be after */
HWND prev = GetWindow(data->hwnd, GW_HWNDPREV);
while (prev && !((GetWindowLongW(prev, GWL_STYLE) & WS_VISIBLE) &&
(prev_window = macdrv_get_cocoa_window(prev))))
prev = GetWindow(prev, GW_HWNDPREV);
if (!prev_window)
{
/* find window that this one must be before */
next = GetWindow(data->hwnd, GW_HWNDNEXT);
while (next && !((GetWindowLongW(next, GWL_STYLE) & WS_VISIBLE) &&
(next_window = macdrv_get_cocoa_window(next))))
next = GetWindow(next, GW_HWNDNEXT);
}
data->on_screen = macdrv_order_cocoa_window(data->cocoa_window, prev_window, next_window);
TRACE("win %p/%p below %p/%p above %p/%p\n",
data->hwnd, data->cocoa_window, prev, prev_window, next, next_window);
}
}
/***********************************************************************
* move_window_bits
*
* Move the window bits when a window is moved.
*/
static void move_window_bits(HWND hwnd, macdrv_window window, const RECT *old_rect, const RECT *new_rect,
const RECT *old_client_rect, const RECT *new_client_rect,
const RECT *new_window_rect)
{
RECT src_rect = *old_rect;
RECT dst_rect = *new_rect;
HDC hdc_src, hdc_dst;
HRGN rgn;
HWND parent = 0;
if (!window)
{
OffsetRect(&dst_rect, -new_window_rect->left, -new_window_rect->top);
parent = GetAncestor(hwnd, GA_PARENT);
hdc_src = GetDCEx(parent, 0, DCX_CACHE);
hdc_dst = GetDCEx(hwnd, 0, DCX_CACHE | DCX_WINDOW);
}
else
{
OffsetRect(&dst_rect, -new_client_rect->left, -new_client_rect->top);
/* make src rect relative to the old position of the window */
OffsetRect(&src_rect, -old_client_rect->left, -old_client_rect->top);
if (dst_rect.left == src_rect.left && dst_rect.top == src_rect.top) return;
hdc_src = hdc_dst = GetDCEx(hwnd, 0, DCX_CACHE);
}
rgn = CreateRectRgnIndirect(&dst_rect);
SelectClipRgn(hdc_dst, rgn);
DeleteObject(rgn);
ExcludeUpdateRgn(hdc_dst, hwnd);
TRACE("copying bits for win %p/%p %s -> %s\n", hwnd, window,
wine_dbgstr_rect(&src_rect), wine_dbgstr_rect(&dst_rect));
BitBlt(hdc_dst, dst_rect.left, dst_rect.top,
dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top,
hdc_src, src_rect.left, src_rect.top, SRCCOPY);
ReleaseDC(hwnd, hdc_dst);
if (hdc_src != hdc_dst) ReleaseDC(parent, hdc_src);
}
/**********************************************************************
* CreateDesktopWindow (MACDRV.@)
*/
BOOL CDECL macdrv_CreateDesktopWindow(HWND hwnd)
{
unsigned int width, height;
TRACE("%p\n", hwnd);
/* retrieve the real size of the desktop */
SERVER_START_REQ(get_window_rectangles)
{
req->handle = wine_server_user_handle(hwnd);
req->relative = COORDS_CLIENT;
wine_server_call(req);
width = reply->window.right;
height = reply->window.bottom;
}
SERVER_END_REQ;
if (!width && !height) /* not initialized yet */
{
CGRect rect = macdrv_get_desktop_rect();
SERVER_START_REQ(set_window_pos)
{
req->handle = wine_server_user_handle(hwnd);
req->previous = 0;
req->swp_flags = SWP_NOZORDER;
req->window.left = CGRectGetMinX(rect);
req->window.top = CGRectGetMinY(rect);
req->window.right = CGRectGetMaxX(rect);
req->window.bottom = CGRectGetMaxY(rect);
req->client = req->window;
wine_server_call(req);
}
SERVER_END_REQ;
}
return TRUE;
}
/**********************************************************************
* CreateWindow (MACDRV.@)
*/
BOOL CDECL macdrv_CreateWindow(HWND hwnd)
{
return TRUE;
}
/***********************************************************************
* DestroyWindow (MACDRV.@)
*/
void CDECL macdrv_DestroyWindow(HWND hwnd)
{
struct macdrv_win_data *data;
TRACE("%p\n", hwnd);
if (!(data = get_win_data(hwnd))) return;
destroy_cocoa_window(data);
CFDictionaryRemoveValue(win_datas, hwnd);
release_win_data(data);
HeapFree(GetProcessHeap(), 0, data);
}
/*****************************************************************
* SetParent (MACDRV.@)
*/
void CDECL macdrv_SetParent(HWND hwnd, HWND parent, HWND old_parent)
{
struct macdrv_win_data *data;
TRACE("%p, %p, %p\n", hwnd, parent, old_parent);
if (parent == old_parent) return;
if (!(data = get_win_data(hwnd))) return;
if (parent != GetDesktopWindow()) /* a child window */
{
if (old_parent == GetDesktopWindow())
{
/* destroy the old Mac window */
destroy_cocoa_window(data);
}
}
else /* new top level window */
create_cocoa_window(data);
release_win_data(data);
}
/***********************************************************************
* SetWindowStyle (MACDRV.@)
*
* Update the state of the Cocoa window to reflect a style change
*/
void CDECL macdrv_SetWindowStyle(HWND hwnd, INT offset, STYLESTRUCT *style)
{
struct macdrv_win_data *data;
TRACE("%p, %d, %p\n", hwnd, offset, style);
if (hwnd == GetDesktopWindow()) return;
if (!(data = get_win_data(hwnd))) return;
if (data->cocoa_window)
set_cocoa_window_properties(data);
release_win_data(data);
}
/*****************************************************************
* SetWindowText (MACDRV.@)
*/
void CDECL macdrv_SetWindowText(HWND hwnd, LPCWSTR text)
{
macdrv_window win;
TRACE("%p, %s\n", hwnd, debugstr_w(text));
if ((win = macdrv_get_cocoa_window(hwnd)))
macdrv_set_cocoa_window_title(win, text, strlenW(text));
}
/***********************************************************************
* WindowPosChanging (MACDRV.@)
*/
void CDECL macdrv_WindowPosChanging(HWND hwnd, HWND insert_after, UINT swp_flags,
const RECT *window_rect, const RECT *client_rect,
RECT *visible_rect, struct window_surface **surface)
{
struct macdrv_win_data *data = get_win_data(hwnd);
DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
TRACE("%p after %p swp %04x window %s client %s visible %s surface %p\n", hwnd, insert_after,
swp_flags, wine_dbgstr_rect(window_rect), wine_dbgstr_rect(client_rect),
wine_dbgstr_rect(visible_rect), surface);
if (!data && !(data = macdrv_create_win_data(hwnd, window_rect, client_rect))) return;
*visible_rect = *window_rect;
macdrv_window_to_mac_rect(data, style, visible_rect);
TRACE("visible_rect %s -> %s\n", wine_dbgstr_rect(window_rect),
wine_dbgstr_rect(visible_rect));
/* release the window surface if necessary */
if (!data->cocoa_window) goto done;
if (swp_flags & SWP_HIDEWINDOW) goto done;
if (*surface) window_surface_release(*surface);
*surface = NULL;
done:
release_win_data(data);
}
/***********************************************************************
* WindowPosChanged (MACDRV.@)
*/
void CDECL macdrv_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags,
const RECT *window_rect, const RECT *client_rect,
const RECT *visible_rect, const RECT *valid_rects,
struct window_surface *surface)
{
struct macdrv_win_data *data;
DWORD new_style = GetWindowLongW(hwnd, GWL_STYLE);
RECT old_window_rect, old_whole_rect, old_client_rect;
if (!(data = get_win_data(hwnd))) return;
old_window_rect = data->window_rect;
old_whole_rect = data->whole_rect;
old_client_rect = data->client_rect;
data->window_rect = *window_rect;
data->whole_rect = *visible_rect;
data->client_rect = *client_rect;
TRACE("win %p/%p window %s whole %s client %s style %08x flags %08x surface %p\n",
hwnd, data->cocoa_window, wine_dbgstr_rect(window_rect),
wine_dbgstr_rect(visible_rect), wine_dbgstr_rect(client_rect),
new_style, swp_flags, surface);
if (!IsRectEmpty(&valid_rects[0]))
{
macdrv_window window = data->cocoa_window;
int x_offset = old_whole_rect.left - data->whole_rect.left;
int y_offset = old_whole_rect.top - data->whole_rect.top;
/* if all that happened is that the whole window moved, copy everything */
if (!(swp_flags & SWP_FRAMECHANGED) &&
old_whole_rect.right - data->whole_rect.right == x_offset &&
old_whole_rect.bottom - data->whole_rect.bottom == y_offset &&
old_client_rect.left - data->client_rect.left == x_offset &&
old_client_rect.right - data->client_rect.right == x_offset &&
old_client_rect.top - data->client_rect.top == y_offset &&
old_client_rect.bottom - data->client_rect.bottom == y_offset &&
!memcmp(&valid_rects[0], &data->client_rect, sizeof(RECT)))
{
/* A Cocoa window's bits are moved automatically */
if (!window && (x_offset != 0 || y_offset != 0))
{
release_win_data(data);
move_window_bits(hwnd, window, &old_whole_rect, visible_rect,
&old_client_rect, client_rect, window_rect);
if (!(data = get_win_data(hwnd))) return;
}
}
else
{
release_win_data(data);
move_window_bits(hwnd, window, &valid_rects[1], &valid_rects[0],
&old_client_rect, client_rect, window_rect);
if (!(data = get_win_data(hwnd))) return;
}
}
if (!data->cocoa_window) goto done;
if (data->on_screen)
{
if ((swp_flags & SWP_HIDEWINDOW) && !(new_style & WS_VISIBLE))
hide_window(data);
}
if (new_style & WS_VISIBLE)
{
if (!data->on_screen || (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)))
set_cocoa_window_properties(data);
if (!data->on_screen)
show_window(data);
}
sync_window_position(data, swp_flags);
set_cocoa_window_properties(data);
done:
release_win_data(data);
}