/* GDK - The GIMP Drawing Kit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * Copyright (C) 1998-2004 Tor Lillqvist * Copyright (C) 2001-2011 Hans Breuer * Copyright (C) 2007-2009 Cody Russell * * 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 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, see . */ /* * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #include "config.h" #include #include "gdk.h" #include "gdkwindowimpl.h" #include "gdkprivate-win32.h" #include "gdkdeviceprivate.h" #include "gdkdevicemanager-win32.h" #include "gdkenumtypes.h" #include "gdkwin32.h" #include "gdkdisplayprivate.h" #include "gdkmonitorprivate.h" #include "gdkwin32window.h" #include "gdkglcontext-win32.h" #include "gdkdisplay-win32.h" #include #include #include #include "fallback-c89.c" static void gdk_window_impl_win32_init (GdkWindowImplWin32 *window); static void gdk_window_impl_win32_class_init (GdkWindowImplWin32Class *klass); static void gdk_window_impl_win32_finalize (GObject *object); static gpointer parent_class = NULL; static GSList *modal_window_stack = NULL; static const cairo_user_data_key_t gdk_win32_cairo_key; typedef struct _FullscreenInfo FullscreenInfo; struct _FullscreenInfo { RECT r; guint hint_flags; LONG style; }; struct _AeroSnapEdgeRegion { /* The rectangle along the edge of the desktop * that allows application of the snap transformation. */ GdkRectangle edge; /* A subregion of the "edge". When the pointer hits * this region, the transformation is revealed. * Usually it is 1-pixel thick and is located at the * very edge of the screen. When there's a toolbar * at that edge, the "trigger" and the "edge" regions * are extended to cover that toolbar. */ GdkRectangle trigger; }; typedef struct _AeroSnapEdgeRegion AeroSnapEdgeRegion; /* Use this for hWndInsertAfter (2nd argument to SetWindowPos()) if * SWP_NOZORDER flag is used. Otherwise it's unobvious why a particular * argument is used. Using NULL is misleading, because * NULL is equivalent to HWND_TOP. */ #define SWP_NOZORDER_SPECIFIED HWND_TOP /* Size of the regions at the edges of the desktop where * snapping can take place (in pixels) */ #define AEROSNAP_REGION_THICKNESS (20) /* Size of the subregions that actually trigger the snapping prompt * (in pixels). */ #define AEROSNAP_REGION_TRIGGER_THICKNESS (1) /* The gap between the snap indicator and the edge of the work area * (in pixels). */ #define AEROSNAP_INDICATOR_EDGE_GAP (10) /* Width of the outline of the snap indicator * (in pixels). */ #define AEROSNAP_INDICATOR_LINE_WIDTH (3.0) /* Corner radius of the snap indicator. */ #define AEROSNAP_INDICATOR_CORNER_RADIUS (3.0) /* The time it takes for snap indicator to expand/shrink * from current window size to future position of the * snapped window (in microseconds). */ #define AEROSNAP_INDICATOR_ANIMATION_DURATION (200 * 1000) /* Opacity if the snap indicator. */ #define AEROSNAP_INDICATOR_OPACITY (0.5) /* The interval between snap indicator redraws (in milliseconds). * 16 is ~ 1/60 of a second, for ~60 FPS. */ #define AEROSNAP_INDICATOR_ANIMATION_TICK (16) static gboolean _gdk_window_get_functions (GdkWindow *window, GdkWMFunction *functions); static HDC _gdk_win32_impl_acquire_dc (GdkWindowImplWin32 *impl); static void _gdk_win32_impl_release_dc (GdkWindowImplWin32 *impl); #define WINDOW_IS_TOPLEVEL(window) \ (GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN) struct _GdkWin32Window { GdkWindow parent; }; struct _GdkWin32WindowClass { GdkWindowClass parent_class; }; G_DEFINE_TYPE (GdkWin32Window, gdk_win32_window, GDK_TYPE_WINDOW) static void gdk_win32_window_class_init (GdkWin32WindowClass *window_class) { } static void gdk_win32_window_init (GdkWin32Window *window) { } G_DEFINE_TYPE (GdkWindowImplWin32, gdk_window_impl_win32, GDK_TYPE_WINDOW_IMPL) GType _gdk_window_impl_win32_get_type (void) { static GType object_type = 0; if (!object_type) { const GTypeInfo object_info = { sizeof (GdkWindowImplWin32Class), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gdk_window_impl_win32_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GdkWindowImplWin32), 0, /* n_preallocs */ (GInstanceInitFunc) gdk_window_impl_win32_init, }; object_type = g_type_register_static (GDK_TYPE_WINDOW_IMPL, "GdkWindowImplWin32", &object_info, 0); } return object_type; } static void gdk_window_impl_win32_init (GdkWindowImplWin32 *impl) { GdkDisplay *display = gdk_display_get_default (); impl->toplevel_window_type = -1; impl->hicon_big = NULL; impl->hicon_small = NULL; impl->hint_flags = 0; impl->type_hint = GDK_WINDOW_TYPE_HINT_NORMAL; impl->transient_owner = NULL; impl->transient_children = NULL; impl->num_transients = 0; impl->changing_state = FALSE; impl->window_scale = 1; } static void gdk_window_impl_win32_finalize (GObject *object) { GdkWindow *wrapper; GdkWindowImplWin32 *window_impl; g_return_if_fail (GDK_IS_WINDOW_IMPL_WIN32 (object)); window_impl = GDK_WINDOW_IMPL_WIN32 (object); wrapper = window_impl->wrapper; if (!GDK_WINDOW_DESTROYED (wrapper)) { gdk_win32_handle_table_remove (window_impl->handle); } g_clear_pointer (&window_impl->snap_stash, g_free); g_clear_pointer (&window_impl->snap_stash_int, g_free); if (window_impl->hicon_big != NULL) { GDI_CALL (DestroyIcon, (window_impl->hicon_big)); window_impl->hicon_big = NULL; } if (window_impl->hicon_small != NULL) { GDI_CALL (DestroyIcon, (window_impl->hicon_small)); window_impl->hicon_small = NULL; } g_free (window_impl->decorations); if (window_impl->cache_surface) { cairo_surface_destroy (window_impl->cache_surface); window_impl->cache_surface = NULL; } if (window_impl->cairo_surface) { cairo_surface_destroy (window_impl->cairo_surface); window_impl->cairo_surface = NULL; } g_assert (window_impl->transient_owner == NULL); g_assert (window_impl->transient_children == NULL); G_OBJECT_CLASS (parent_class)->finalize (object); } static void gdk_win32_get_window_client_area_rect (GdkWindow *window, gint scale, RECT *rect) { gint x, y, width, height; gdk_window_get_position (window, &x, &y); width = gdk_window_get_width (window); height = gdk_window_get_height (window); rect->left = x * scale; rect->top = y * scale; rect->right = rect->left + width * scale; rect->bottom = rect->top + height * scale; } static void gdk_win32_window_get_queued_window_rect (GdkWindow *window, RECT *return_window_rect) { RECT window_rect; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); gdk_win32_get_window_client_area_rect (window, impl->window_scale, &window_rect); /* Turn client area into window area */ _gdk_win32_adjust_client_rect (window, &window_rect); /* Convert GDK screen coordinates to W32 desktop coordinates */ window_rect.left -= _gdk_offset_x * impl->window_scale; window_rect.right -= _gdk_offset_x * impl->window_scale; window_rect.top -= _gdk_offset_y * impl->window_scale; window_rect.bottom -= _gdk_offset_y * impl->window_scale; *return_window_rect = window_rect; } static void gdk_win32_window_apply_queued_move_resize (GdkWindow *window, RECT window_rect) { if (!IsIconic (GDK_WINDOW_HWND (window))) { GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); GDK_NOTE (EVENTS, g_print ("Setting window position ... ")); API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), SWP_NOZORDER_SPECIFIED, window_rect.left, window_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW)); GDK_NOTE (EVENTS, g_print (" ... set window position\n")); return; } /* Don't move iconic windows */ /* TODO: use SetWindowPlacement() to change non-minimized window position */ } static gboolean gdk_win32_window_begin_paint (GdkWindow *window) { GdkWindowImplWin32 *impl; RECT window_rect; if (window == NULL || GDK_WINDOW_DESTROYED (window)) return TRUE; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); /* Layered windows are moved *after* repaint. * We supply our own surface, return FALSE to make GDK use it. */ if (impl->layered) return FALSE; /* FIXME: Possibly remove the following lines when we transition to GL * drawing fully. This will probably mean that we won't * be able to use layered windows, as layered windows seem * to support only up to OpenGL 1.1, which is not enough for our * needs here. */ /* Non-GL windows are moved *after* repaint. * We don't supply our own surface, return TRUE to make GDK create * one by itself. *//* if (!window->current_paint.use_gl) return TRUE;*/ /* GL windows are moved *before* repaint (otherwise * repainting doesn't work), but if there's no move queued up, * return immediately. Doesn't matter what we return, GDK * will create a surface anyway, as if we returned TRUE. */ if (!impl->drag_move_resize_context.native_move_resize_pending) return TRUE; impl->drag_move_resize_context.native_move_resize_pending = FALSE; /* Get the position/size of the window that GDK wants, * apply it. */ gdk_win32_window_get_queued_window_rect (window, &window_rect); gdk_win32_window_apply_queued_move_resize (window, window_rect); return TRUE; } static void gdk_win32_window_end_paint (GdkWindow *window) { /* FIXME: Possibly make gdk_win32_window_end_paint() a * no-op stub, like what is done in Wayland, as * the items here rely on layered window usage, * when we transition to full GL drawing, as * layered windows do not support enough GL * for our needs here */ GdkWindowImplWin32 *impl; RECT window_rect; HDC hdc; POINT window_position; SIZE window_size; POINT source_point; BLENDFUNCTION blender; cairo_t *cr; if (window == NULL || GDK_WINDOW_DESTROYED (window)) return; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); /* GL windows are moved *before* repaint */ /*if (window->current_paint.use_gl) return;*/ /* No move/resize is queued up, and we don't need to update * the contents of a layered window, so return immediately. */ if (!impl->layered && !impl->drag_move_resize_context.native_move_resize_pending) return; impl->drag_move_resize_context.native_move_resize_pending = FALSE; /* Get the position/size of the window that GDK wants. */ gdk_win32_window_get_queued_window_rect (window, &window_rect); if (!impl->layered) { gdk_win32_window_apply_queued_move_resize (window, window_rect); return; } window_position.x = window_rect.left; window_position.y = window_rect.top; window_size.cx = window_rect.right - window_rect.left; window_size.cy = window_rect.bottom - window_rect.top; cairo_surface_flush (impl->cairo_surface); /* we always draw in the top-left corner of the surface */ source_point.x = source_point.y = 0; blender.BlendOp = AC_SRC_OVER; blender.BlendFlags = 0; blender.AlphaFormat = AC_SRC_ALPHA; blender.SourceConstantAlpha = impl->layered_opacity * 255; /* Update cache surface contents */ cr = cairo_create (impl->cache_surface); cairo_set_source_surface (cr, window->current_paint.surface, 0, 0); gdk_cairo_region (cr, window->current_paint.region); cairo_clip (cr); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_paint (cr); cairo_destroy (cr); cairo_surface_flush (impl->cache_surface); hdc = cairo_win32_surface_get_dc (impl->cache_surface); /* Don't use UpdateLayeredWindow on minimized windows */ if (IsIconic (GDK_WINDOW_HWND (window))) { gdk_win32_window_apply_queued_move_resize (window, window_rect); return; } /* Move, resize and redraw layered window in one call */ API_CALL (UpdateLayeredWindow, (GDK_WINDOW_HWND (window), NULL, &window_position, &window_size, hdc, &source_point, 0, &blender, ULW_ALPHA)); } void _gdk_win32_adjust_client_rect (GdkWindow *window, RECT *rect) { LONG style, exstyle; style = GetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE); exstyle = GetWindowLong (GDK_WINDOW_HWND (window), GWL_EXSTYLE); API_CALL (AdjustWindowRectEx, (rect, style, FALSE, exstyle)); } gboolean _gdk_win32_window_enable_transparency (GdkWindow *window) { GdkWindowImplWin32 *impl; DWM_BLURBEHIND blur_behind; HRGN empty_region; HRESULT call_result; HWND parent, thiswindow; if (window == NULL || GDK_WINDOW_HWND (window) == NULL) return FALSE; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); /* layered windows don't need blurbehind for transparency */ if (impl->layered) return TRUE; if (!gdk_display_is_composited (gdk_window_get_display (window))) return FALSE; thiswindow = GDK_WINDOW_HWND (window); /* Blurbehind only works on toplevel windows */ parent = GetAncestor (thiswindow, GA_PARENT); if (!(GetWindowLong (thiswindow, GWL_STYLE) & WS_POPUP) && (parent == NULL || parent != GetDesktopWindow ())) return FALSE; empty_region = CreateRectRgn (0, 0, -1, -1); if (empty_region == NULL) return FALSE; memset (&blur_behind, 0, sizeof (blur_behind)); blur_behind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; blur_behind.hRgnBlur = empty_region; blur_behind.fEnable = TRUE; call_result = DwmEnableBlurBehindWindow (thiswindow, &blur_behind); if (!SUCCEEDED (call_result)) g_warning ("%s: %s (%p) failed: %" G_GINT32_MODIFIER "x", G_STRLOC, "DwmEnableBlurBehindWindow", thiswindow, (guint32) call_result); DeleteObject (empty_region); return SUCCEEDED (call_result); } static const gchar * get_default_title (void) { const char *title; title = g_get_application_name (); if (!title) title = g_get_prgname (); return title; } /* RegisterGdkClass * is a wrapper function for RegisterWindowClassEx. * It creates at least one unique class for every * GdkWindowType. If support for single window-specific icons * is ever needed (e.g Dialog specific), every such window should * get its own class */ static ATOM RegisterGdkClass (GdkWindowType wtype, GdkWindowTypeHint wtype_hint) { static ATOM klassTOPLEVEL = 0; static ATOM klassCHILD = 0; static ATOM klassTEMP = 0; static ATOM klassTEMPSHADOW = 0; static HICON hAppIcon = NULL; static HICON hAppIconSm = NULL; static WNDCLASSEXW wcl; ATOM klass = 0; wcl.cbSize = sizeof (WNDCLASSEX); wcl.style = 0; /* DON'T set CS_REDRAW. It causes total redraw * on WM_SIZE and WM_MOVE. Flicker, Performance! */ wcl.lpfnWndProc = _gdk_win32_window_procedure; wcl.cbClsExtra = 0; wcl.cbWndExtra = 0; wcl.hInstance = _gdk_app_hmodule; wcl.hIcon = 0; wcl.hIconSm = 0; /* initialize once! */ if (0 == hAppIcon && 0 == hAppIconSm) { gchar sLoc [MAX_PATH+1]; if (0 != GetModuleFileName (_gdk_app_hmodule, sLoc, MAX_PATH)) { ExtractIconEx (sLoc, 0, &hAppIcon, &hAppIconSm, 1); if (0 == hAppIcon && 0 == hAppIconSm) { if (0 != GetModuleFileName (_gdk_dll_hinstance, sLoc, MAX_PATH)) { ExtractIconEx (sLoc, 0, &hAppIcon, &hAppIconSm, 1); } } } if (0 == hAppIcon && 0 == hAppIconSm) { hAppIcon = LoadImage (NULL, IDI_APPLICATION, IMAGE_ICON, GetSystemMetrics (SM_CXICON), GetSystemMetrics (SM_CYICON), 0); hAppIconSm = LoadImage (NULL, IDI_APPLICATION, IMAGE_ICON, GetSystemMetrics (SM_CXSMICON), GetSystemMetrics (SM_CYSMICON), 0); } } if (0 == hAppIcon) hAppIcon = hAppIconSm; else if (0 == hAppIconSm) hAppIconSm = hAppIcon; wcl.lpszMenuName = NULL; /* initialize once per class */ /* * HB: Setting the background brush leads to flicker, because we * don't get asked how to clear the background. This is not what * we want, at least not for input_only windows ... */ #define ONCE_PER_CLASS() \ wcl.hIcon = CopyIcon (hAppIcon); \ wcl.hIconSm = CopyIcon (hAppIconSm); \ wcl.hbrBackground = NULL; \ wcl.hCursor = LoadCursor (NULL, IDC_ARROW); switch (wtype) { case GDK_WINDOW_TOPLEVEL: /* MSDN: CS_OWNDC is needed for OpenGL contexts */ wcl.style |= CS_OWNDC; if (0 == klassTOPLEVEL) { wcl.lpszClassName = L"gdkWindowToplevel"; ONCE_PER_CLASS (); klassTOPLEVEL = RegisterClassExW (&wcl); } klass = klassTOPLEVEL; break; case GDK_WINDOW_TEMP: if ((wtype_hint == GDK_WINDOW_TYPE_HINT_MENU) || (wtype_hint == GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU) || (wtype_hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU)) { if (klassTEMPSHADOW == 0) { wcl.lpszClassName = L"gdkWindowTempShadow"; wcl.style |= CS_SAVEBITS; wcl.style |= 0x00020000; /* CS_DROPSHADOW */ ONCE_PER_CLASS (); klassTEMPSHADOW = RegisterClassExW (&wcl); } klass = klassTEMPSHADOW; } else { if (klassTEMP == 0) { wcl.lpszClassName = L"gdkWindowTemp"; wcl.style |= CS_SAVEBITS; ONCE_PER_CLASS (); klassTEMP = RegisterClassExW (&wcl); } klass = klassTEMP; } break; case GDK_WINDOW_CHILD: default: g_assert_not_reached (); break; } if (klass == 0) { WIN32_API_FAILED ("RegisterClassExW"); g_error ("That is a fatal error"); } return klass; } /* * Create native windows. * * With the default Gdk the created windows are mostly toplevel windows. * * Placement of the window is derived from the passed in window, * except for toplevel window where OS/Window Manager placement * is used. * * From attributes the only things used is: colormap, title, * wmclass and type_hint. [1]. We are checking redundant information * and complain if that changes, which would break this implementation * again. * * [1] http://mail.gnome.org/archives/gtk-devel-list/2010-August/msg00214.html */ void _gdk_win32_display_create_window_impl (GdkDisplay *display, GdkWindow *window, GdkWindow *real_parent, GdkEventMask event_mask, GdkWindowAttr *attributes) { HWND hwndNew; HANDLE hparent; ATOM klass = 0; DWORD dwStyle = 0, dwExStyle; RECT rect; GdkWindowImplWin32 *impl; GdkWin32Display *display_win32; const gchar *title; wchar_t *wtitle; gint window_width, window_height; gint offset_x = 0, offset_y = 0; gint x, y, real_x = 0, real_y = 0; g_return_if_fail (display == _gdk_display); GDK_NOTE (MISC, g_print ("_gdk_window_impl_new: %s %s\n", (window->window_type == GDK_WINDOW_TOPLEVEL ? "TOPLEVEL" : (window->window_type == GDK_WINDOW_TEMP ? "TEMP" : "???")), (attributes->wclass == GDK_INPUT_OUTPUT ? "" : "input-only"))); hparent = (real_parent != NULL) ? GDK_WINDOW_HWND (real_parent) : NULL; impl = g_object_new (GDK_TYPE_WINDOW_IMPL_WIN32, NULL); impl->wrapper = GDK_WINDOW (window); window->impl = GDK_WINDOW_IMPL (impl); impl->layered = FALSE; impl->layered_opacity = 1.0; display_win32 = GDK_WIN32_DISPLAY (display); impl->window_scale = _gdk_win32_display_get_monitor_scale_factor (display_win32, NULL, NULL, NULL); impl->unscaled_width = window->width * impl->window_scale; impl->unscaled_height = window->height * impl->window_scale; if (!window->input_only) { dwExStyle = 0; } else { /* I very much doubt using WS_EX_TRANSPARENT actually * corresponds to how X11 InputOnly windows work, but it appears * to work well enough for the actual use cases in gtk. */ dwExStyle = WS_EX_TRANSPARENT; GDK_NOTE (MISC, g_print ("... GDK_INPUT_ONLY\n")); } switch (window->window_type) { case GDK_WINDOW_TOPLEVEL: if (window->parent) { /* The common code warns for this case. */ hparent = GetDesktopWindow (); } /* Children of foreign windows aren't toplevel windows */ if (real_parent != NULL && GDK_WINDOW_TYPE (real_parent) == GDK_WINDOW_FOREIGN) { dwStyle = WS_CHILDWINDOW | WS_CLIPCHILDREN; } else { /* MSDN: We need WS_CLIPCHILDREN and WS_CLIPSIBLINGS for GL Context Creation */ if (window->window_type == GDK_WINDOW_TOPLEVEL) dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; else dwStyle = WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU | WS_CAPTION | WS_THICKFRAME | WS_CLIPCHILDREN; offset_x = _gdk_offset_x; offset_y = _gdk_offset_y; } break; case GDK_WINDOW_TEMP: /* A temp window is not necessarily a top level window */ dwStyle = real_parent == NULL ? WS_POPUP : WS_CHILDWINDOW; dwStyle |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS; dwExStyle |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; offset_x = _gdk_offset_x; offset_y = _gdk_offset_y; break; case GDK_WINDOW_CHILD: default: g_assert_not_reached (); } rect.left = window->x * impl->window_scale; rect.top = window->y * impl->window_scale; rect.right = rect.left + window->width * impl->window_scale; rect.bottom = rect.top + window->height * impl->window_scale; AdjustWindowRectEx (&rect, dwStyle, FALSE, dwExStyle); real_x = (window->x - offset_x) * impl->window_scale; real_y = (window->y - offset_y) * impl->window_scale; if (window->window_type == GDK_WINDOW_TOPLEVEL) { /* We initially place it at default so that we can get the default window positioning if we want */ x = y = CW_USEDEFAULT; } else { /* TEMP, FOREIGN: Put these where requested */ x = real_x; y = real_y; } window_width = rect.right - rect.left; window_height = rect.bottom - rect.top; title = get_default_title (); if (!title || !*title) title = ""; impl->native_event_mask = GDK_STRUCTURE_MASK | event_mask; if (impl->type_hint == GDK_WINDOW_TYPE_HINT_UTILITY) dwExStyle |= WS_EX_TOOLWINDOW; /* WS_EX_TRANSPARENT means "try draw this window last, and ignore input". * It's the last part we're after. We don't want DND indicator to accept * input, because that will make it a potential drop target, and if it's * under the mouse cursor, this will kill any DND. */ if (impl->type_hint == GDK_WINDOW_TYPE_HINT_DND) dwExStyle |= WS_EX_TRANSPARENT; klass = RegisterGdkClass (window->window_type, impl->type_hint); wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL); hwndNew = CreateWindowExW (dwExStyle, MAKEINTRESOURCEW (klass), wtitle, dwStyle, x, y, window_width, window_height, hparent, NULL, _gdk_app_hmodule, window); if (GDK_WINDOW_HWND (window) != hwndNew) { g_warning ("gdk_window_new: gdk_event_translate::WM_CREATE (%p, %p) HWND mismatch.", GDK_WINDOW_HWND (window), hwndNew); /* HB: IHMO due to a race condition the handle was increased by * one, which causes much trouble. Because I can't find the * real bug, try to workaround it ... * To reproduce: compile with MSVC 5, DEBUG=1 */ # if 0 gdk_win32_handle_table_remove (GDK_WINDOW_HWND (window)); GDK_WINDOW_HWND (window) = hwndNew; gdk_win32_handle_table_insert (&GDK_WINDOW_HWND (window), window); # else /* the old behaviour, but with warning */ impl->handle = hwndNew; # endif } GetWindowRect (GDK_WINDOW_HWND (window), &rect); impl->initial_x = rect.left; impl->initial_y = rect.top; /* Now we know the initial position, move to actually specified position */ if (real_x != x || real_y != y) { API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), SWP_NOZORDER_SPECIFIED, real_x, real_y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); } g_object_ref (window); gdk_win32_handle_table_insert (&GDK_WINDOW_HWND (window), window); GDK_NOTE (MISC, g_print ("... \"%s\" %dx%d@%+d%+d %p = %p\n", title, window_width, window_height, window->x - offset_x, window->y - offset_y, hparent, GDK_WINDOW_HWND (window))); /* Add window handle to title */ GDK_NOTE (MISC_OR_EVENTS, gdk_window_set_title (window, title)); g_free (wtitle); if (impl->handle == NULL) { WIN32_API_FAILED ("CreateWindowExW"); g_object_unref (window); return; } // if (!from_set_skip_taskbar_hint && window->window_type == GDK_WINDOW_TEMP) // gdk_window_set_skip_taskbar_hint (window, TRUE); _gdk_win32_window_enable_transparency (window); } GdkWindow * gdk_win32_window_foreign_new_for_display (GdkDisplay *display, HWND anid) { GdkWindow *window; GdkWindowImplWin32 *impl; HANDLE parent; RECT rect; POINT point; if ((window = gdk_win32_window_lookup_for_display (display, anid)) != NULL) return g_object_ref (window); window = _gdk_display_create_window (display); window->impl = g_object_new (GDK_TYPE_WINDOW_IMPL_WIN32, NULL); window->impl_window = window; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); impl->wrapper = window; parent = GetParent (anid); /* Always treat foreigns as toplevels */ window->parent = NULL; GetClientRect ((HWND) anid, &rect); point.x = rect.left; point.y = rect.right; ClientToScreen ((HWND) anid, &point); if (parent != GetDesktopWindow ()) ScreenToClient (parent, &point); window->x = point.x / impl->window_scale; window->y = point.y / impl->window_scale; impl->unscaled_width = rect.right - rect.left; impl->unscaled_height = rect.bottom - rect.top; window->width = (impl->unscaled_width + impl->window_scale - 1) / impl->window_scale; window->height = (impl->unscaled_height + impl->window_scale - 1) / impl->window_scale; window->window_type = GDK_WINDOW_FOREIGN; window->destroyed = FALSE; window->event_mask = GDK_ALL_EVENTS_MASK; /* XXX */ if (IsWindowVisible ((HWND) anid)) window->state &= (~GDK_WINDOW_STATE_WITHDRAWN); else window->state |= GDK_WINDOW_STATE_WITHDRAWN; if (GetWindowLong ((HWND)anid, GWL_EXSTYLE) & WS_EX_TOPMOST) window->state |= GDK_WINDOW_STATE_ABOVE; else window->state &= (~GDK_WINDOW_STATE_ABOVE); window->state &= (~GDK_WINDOW_STATE_BELOW); window->viewable = TRUE; GDK_WINDOW_HWND (window) = anid; g_object_ref (window); gdk_win32_handle_table_insert (&GDK_WINDOW_HWND (window), window); GDK_NOTE (MISC, g_print ("gdk_win32_window_foreign_new_for_display: %p: %s@%+d%+d\n", (HWND) anid, _gdk_win32_window_description (window), window->x, window->y)); return window; } static void gdk_win32_window_destroy (GdkWindow *window, gboolean recursing, gboolean foreign_destroy) { GdkWindowImplWin32 *window_impl = GDK_WINDOW_IMPL_WIN32 (window->impl); GSList *tmp; g_return_if_fail (GDK_IS_WINDOW (window)); GDK_NOTE (MISC, g_print ("gdk_win32_window_destroy: %p\n", GDK_WINDOW_HWND (window))); /* Remove ourself from the modal stack */ _gdk_remove_modal_window (window); /* Remove all our transient children */ tmp = window_impl->transient_children; while (tmp != NULL) { GdkWindow *child = tmp->data; GdkWindowImplWin32 *child_impl = GDK_WINDOW_IMPL_WIN32 (GDK_WINDOW (child)->impl); child_impl->transient_owner = NULL; tmp = tmp->next; } g_slist_free (window_impl->transient_children); window_impl->transient_children = NULL; /* Remove ourself from our transient owner */ if (window_impl->transient_owner != NULL) { gdk_window_set_transient_for (window, NULL); } if (!recursing && !foreign_destroy) { window->destroyed = TRUE; DestroyWindow (GDK_WINDOW_HWND (window)); } } /* This function is called when the window really gone. */ static void gdk_win32_window_destroy_notify (GdkWindow *window) { g_return_if_fail (GDK_IS_WINDOW (window)); GDK_NOTE (EVENTS, g_print ("gdk_window_destroy_notify: %p%s\n", GDK_WINDOW_HWND (window), (GDK_WINDOW_DESTROYED (window) ? " (destroyed)" : ""))); if (!GDK_WINDOW_DESTROYED (window)) { if (GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN) g_warning ("window %p unexpectedly destroyed", GDK_WINDOW_HWND (window)); _gdk_window_destroy (window, TRUE); } gdk_win32_handle_table_remove (GDK_WINDOW_HWND (window)); g_object_unref (window); } static void get_outer_rect (GdkWindow *window, gint width, gint height, RECT *rect) { GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); rect->left = rect->top = 0; rect->right = width * impl->window_scale; rect->bottom = height * impl->window_scale; _gdk_win32_adjust_client_rect (window, rect); } static void adjust_for_gravity_hints (GdkWindow *window, RECT *outer_rect, gint *x, gint *y) { GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); if (impl->hint_flags & GDK_HINT_WIN_GRAVITY) { #ifdef G_ENABLE_DEBUG gint orig_x = *x, orig_y = *y; #endif switch (impl->hints.win_gravity) { case GDK_GRAVITY_NORTH: case GDK_GRAVITY_CENTER: case GDK_GRAVITY_SOUTH: *x -= (outer_rect->right - outer_rect->left / 2) / impl->window_scale; *x += window->width / 2; break; case GDK_GRAVITY_SOUTH_EAST: case GDK_GRAVITY_EAST: case GDK_GRAVITY_NORTH_EAST: *x -= (outer_rect->right - outer_rect->left) / impl->window_scale; *x += window->width; break; case GDK_GRAVITY_STATIC: *x += outer_rect->left / impl->window_scale; break; default: break; } switch (impl->hints.win_gravity) { case GDK_GRAVITY_WEST: case GDK_GRAVITY_CENTER: case GDK_GRAVITY_EAST: *y -= ((outer_rect->bottom - outer_rect->top) / 2) / impl->window_scale; *y += window->height / 2; break; case GDK_GRAVITY_SOUTH_WEST: case GDK_GRAVITY_SOUTH: case GDK_GRAVITY_SOUTH_EAST: *y -= (outer_rect->bottom - outer_rect->top) / impl->window_scale; *y += window->height; break; case GDK_GRAVITY_STATIC: *y += outer_rect->top * impl->window_scale; break; default: break; } GDK_NOTE (MISC, (orig_x != *x || orig_y != *y) ? g_print ("adjust_for_gravity_hints: x: %d->%d, y: %d->%d\n", orig_x, *x, orig_y, *y) : (void) 0); } } static void show_window_internal (GdkWindow *window, gboolean already_mapped, gboolean deiconify) { GdkWindowImplWin32 *window_impl; gboolean focus_on_map = FALSE; DWORD exstyle; if (window->destroyed) return; GDK_NOTE (MISC, g_print ("show_window_internal: %p: %s%s\n", GDK_WINDOW_HWND (window), _gdk_win32_window_state_to_string (window->state), (deiconify ? " deiconify" : ""))); /* If asked to show (not deiconify) an withdrawn and iconified * window, do that. */ if (!deiconify && !already_mapped && (window->state & GDK_WINDOW_STATE_ICONIFIED)) { GtkShowWindow (window, SW_SHOWMINNOACTIVE); return; } /* If asked to just show an iconified window, do nothing. */ if (!deiconify && (window->state & GDK_WINDOW_STATE_ICONIFIED)) return; /* If asked to deiconify an already noniconified window, do * nothing. (Especially, don't cause the window to rise and * activate. There are different calls for that.) */ if (deiconify && !(window->state & GDK_WINDOW_STATE_ICONIFIED)) return; /* If asked to show (but not raise) a window that is already * visible, do nothing. */ if (!deiconify && !already_mapped && IsWindowVisible (GDK_WINDOW_HWND (window))) return; /* Other cases */ if (!already_mapped) focus_on_map = window->focus_on_map; exstyle = GetWindowLong (GDK_WINDOW_HWND (window), GWL_EXSTYLE); /* Use SetWindowPos to show transparent windows so automatic redraws * in other windows can be suppressed. */ if (exstyle & WS_EX_TRANSPARENT) { UINT flags = SWP_SHOWWINDOW | SWP_NOREDRAW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER; if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_TEMP || !focus_on_map) flags |= SWP_NOACTIVATE; SetWindowPos (GDK_WINDOW_HWND (window), SWP_NOZORDER_SPECIFIED, 0, 0, 0, 0, flags); return; } /* For initial map of "normal" windows we want to emulate WM window * positioning behaviour, which means: * + Use user specified position if GDK_HINT_POS or GDK_HINT_USER_POS * otherwise: * + default to the initial CW_USEDEFAULT placement, * no matter if the user moved the window before showing it. * + Certain window types and hints have more elaborate positioning * schemes. */ window_impl = GDK_WINDOW_IMPL_WIN32 (window->impl); if (!already_mapped && GDK_WINDOW_TYPE (window) == GDK_WINDOW_TOPLEVEL && (window_impl->hint_flags & (GDK_HINT_POS | GDK_HINT_USER_POS)) == 0) { gboolean center = FALSE; RECT window_rect, center_on_rect; int x, y; x = window_impl->initial_x; y = window_impl->initial_y; if (window_impl->type_hint == GDK_WINDOW_TYPE_HINT_SPLASHSCREEN) { HMONITOR monitor; MONITORINFO mi; monitor = MonitorFromWindow (GDK_WINDOW_HWND (window), MONITOR_DEFAULTTONEAREST); mi.cbSize = sizeof (mi); if (monitor && GetMonitorInfo (monitor, &mi)) center_on_rect = mi.rcMonitor; else { center_on_rect.left = 0; center_on_rect.top = 0; center_on_rect.right = GetSystemMetrics (SM_CXSCREEN); center_on_rect.bottom = GetSystemMetrics (SM_CYSCREEN); } center = TRUE; } else if (window_impl->transient_owner != NULL && GDK_WINDOW_IS_MAPPED (window_impl->transient_owner)) { GdkWindow *owner = window_impl->transient_owner; /* Center on transient parent */ center_on_rect.left = (owner->x - _gdk_offset_x) * window_impl->window_scale; center_on_rect.top = (owner->y - _gdk_offset_y) * window_impl->window_scale; center_on_rect.right = center_on_rect.left + owner->width * window_impl->window_scale; center_on_rect.bottom = center_on_rect.top + owner->height * window_impl->window_scale; _gdk_win32_adjust_client_rect (GDK_WINDOW (owner), ¢er_on_rect); center = TRUE; } if (center) { window_rect.left = 0; window_rect.top = 0; window_rect.right = window->width * window_impl->window_scale; window_rect.bottom = window->height * window_impl->window_scale; _gdk_win32_adjust_client_rect (window, &window_rect); x = center_on_rect.left + ((center_on_rect.right - center_on_rect.left) - (window_rect.right - window_rect.left)) / 2; y = center_on_rect.top + ((center_on_rect.bottom - center_on_rect.top) - (window_rect.bottom - window_rect.top)) / 2; } API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), SWP_NOZORDER_SPECIFIED, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); } if (!already_mapped && GDK_WINDOW_TYPE (window) == GDK_WINDOW_TOPLEVEL) { /* Ensure new windows are fully onscreen */ RECT window_rect; HMONITOR monitor; MONITORINFO mi; int x, y; GetWindowRect (GDK_WINDOW_HWND (window), &window_rect); monitor = MonitorFromWindow (GDK_WINDOW_HWND (window), MONITOR_DEFAULTTONEAREST); mi.cbSize = sizeof (mi); if (monitor && GetMonitorInfo (monitor, &mi)) { x = window_rect.left; y = window_rect.top; if (window_rect.right > mi.rcWork.right) { window_rect.left -= (window_rect.right - mi.rcWork.right); window_rect.right -= (window_rect.right - mi.rcWork.right); } if (window_rect.bottom > mi.rcWork.bottom) { window_rect.top -= (window_rect.bottom - mi.rcWork.bottom); window_rect.bottom -= (window_rect.bottom - mi.rcWork.bottom); } if (window_rect.left < mi.rcWork.left) { window_rect.right += (mi.rcWork.left - window_rect.left); window_rect.left += (mi.rcWork.left - window_rect.left); } if (window_rect.top < mi.rcWork.top) { window_rect.bottom += (mi.rcWork.top - window_rect.top); window_rect.top += (mi.rcWork.top - window_rect.top); } if (x != window_rect.left || y != window_rect.top) API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), SWP_NOZORDER_SPECIFIED, window_rect.left, window_rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); } } if (window->state & GDK_WINDOW_STATE_FULLSCREEN) { gdk_window_fullscreen (window); } else if (window->state & GDK_WINDOW_STATE_MAXIMIZED) { GtkShowWindow (window, SW_MAXIMIZE); } else if (window->state & GDK_WINDOW_STATE_ICONIFIED) { if (focus_on_map) GtkShowWindow (window, SW_RESTORE); else GtkShowWindow (window, SW_SHOWNOACTIVATE); } else if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_TEMP || !focus_on_map) { if (!IsWindowVisible (GDK_WINDOW_HWND (window))) GtkShowWindow (window, SW_SHOWNOACTIVATE); else GtkShowWindow (window, SW_SHOWNA); } else if (!IsWindowVisible (GDK_WINDOW_HWND (window))) { GtkShowWindow (window, SW_SHOWNORMAL); } else { GtkShowWindow (window, SW_SHOW); } /* Sync STATE_ABOVE to TOPMOST */ if (GDK_WINDOW_TYPE (window) != GDK_WINDOW_TEMP && (((window->state & GDK_WINDOW_STATE_ABOVE) && !(exstyle & WS_EX_TOPMOST)) || (!(window->state & GDK_WINDOW_STATE_ABOVE) && (exstyle & WS_EX_TOPMOST)))) { API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), (window->state & GDK_WINDOW_STATE_ABOVE)?HWND_TOPMOST:HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)); } } static void gdk_win32_window_show (GdkWindow *window, gboolean already_mapped) { show_window_internal (window, FALSE, FALSE); } static void gdk_win32_window_hide (GdkWindow *window) { if (window->destroyed) return; GDK_NOTE (MISC, g_print ("gdk_win32_window_hide: %p: %s\n", GDK_WINDOW_HWND (window), _gdk_win32_window_state_to_string (window->state))); if (GDK_WINDOW_IS_MAPPED (window)) gdk_synthesize_window_state (window, 0, GDK_WINDOW_STATE_WITHDRAWN); _gdk_window_clear_update_area (window); if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_TOPLEVEL) ShowOwnedPopups (GDK_WINDOW_HWND (window), FALSE); /* Use SetWindowPos to hide transparent windows so automatic redraws * in other windows can be suppressed. */ if (GetWindowLong (GDK_WINDOW_HWND (window), GWL_EXSTYLE) & WS_EX_TRANSPARENT) { SetWindowPos (GDK_WINDOW_HWND (window), SWP_NOZORDER_SPECIFIED, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOREDRAW | SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE); } else { GtkShowWindow (window, SW_HIDE); } } static void gdk_win32_window_withdraw (GdkWindow *window) { if (window->destroyed) return; GDK_NOTE (MISC, g_print ("gdk_win32_window_withdraw: %p: %s\n", GDK_WINDOW_HWND (window), _gdk_win32_window_state_to_string (window->state))); gdk_window_hide (window); /* ??? */ } static void gdk_win32_window_move (GdkWindow *window, gint x, gint y) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_win32_window_move: %p: %+d%+d\n", GDK_WINDOW_HWND (window), x, y)); if (window->state & GDK_WINDOW_STATE_FULLSCREEN) return; /* Don't check GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD. * Foreign windows (another app's windows) might be children of our * windows! Especially in the case of gtkplug/socket. */ if (GetAncestor (GDK_WINDOW_HWND (window), GA_PARENT) != GetDesktopWindow ()) { _gdk_window_move_resize_child (window, x, y, window->width, window->height); } else { RECT outer_rect; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); get_outer_rect (window, window->width, window->height, &outer_rect); adjust_for_gravity_hints (window, &outer_rect, &x, &y); GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,%d,%d,0,0," "NOACTIVATE|NOSIZE|NOZORDER)\n", GDK_WINDOW_HWND (window), (x - _gdk_offset_x) * impl->window_scale, (y - _gdk_offset_y) * impl->window_scale)); API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), SWP_NOZORDER_SPECIFIED, (x - _gdk_offset_x) * impl->window_scale, (y - _gdk_offset_y) * impl->window_scale, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); } } static void gdk_win32_window_resize (GdkWindow *window, gint width, gint height) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; if (width < 1) width = 1; if (height < 1) height = 1; GDK_NOTE (MISC, g_print ("gdk_win32_window_resize: %p: %dx%d\n", GDK_WINDOW_HWND (window), width, height)); if (window->state & GDK_WINDOW_STATE_FULLSCREEN) return; if (GetAncestor (GDK_WINDOW_HWND (window), GA_PARENT) != GetDesktopWindow ()) { _gdk_window_move_resize_child (window, window->x, window->y, width, height); } else { RECT outer_rect; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); get_outer_rect (window, width, height, &outer_rect); GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,0,0,%ld,%ld," "NOACTIVATE|NOMOVE|NOZORDER)\n", GDK_WINDOW_HWND (window), outer_rect.right - outer_rect.left, outer_rect.bottom - outer_rect.top)); API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), SWP_NOZORDER_SPECIFIED, 0, 0, outer_rect.right - outer_rect.left, outer_rect.bottom - outer_rect.top, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER)); window->resize_count += 1; } } static void gdk_win32_window_move_resize_internal (GdkWindow *window, gint x, gint y, gint width, gint height) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; if (width < 1) width = 1; if (height < 1) height = 1; if (window->state & GDK_WINDOW_STATE_FULLSCREEN) return; GDK_NOTE (MISC, g_print ("gdk_win32_window_move_resize: %p: %dx%d@%+d%+d\n", GDK_WINDOW_HWND (window), width, height, x, y)); if (GetAncestor (GDK_WINDOW_HWND (window), GA_PARENT) != GetDesktopWindow ()) { _gdk_window_move_resize_child (window, x, y, width, height); } else { RECT outer_rect; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); get_outer_rect (window, width, height, &outer_rect); adjust_for_gravity_hints (window, &outer_rect, &x, &y); GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,%d,%d,%ld,%ld," "NOACTIVATE|NOZORDER)\n", GDK_WINDOW_HWND (window), (x - _gdk_offset_x) * impl->window_scale, (y - _gdk_offset_y) * impl->window_scale, outer_rect.right - outer_rect.left, outer_rect.bottom - outer_rect.top)); API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), SWP_NOZORDER_SPECIFIED, (x - _gdk_offset_x) * impl->window_scale, (y - _gdk_offset_y) * impl->window_scale, outer_rect.right - outer_rect.left, outer_rect.bottom - outer_rect.top, SWP_NOACTIVATE | SWP_NOZORDER)); } } static void gdk_win32_window_move_resize (GdkWindow *window, gboolean with_move, gint x, gint y, gint width, gint height) { GdkWindowImplWin32 *window_impl; window_impl = GDK_WINDOW_IMPL_WIN32 (window->impl); window_impl->inhibit_configure = TRUE; /* We ignore changes to the window being moved or resized by the user, as we don't want to fight the user */ if (GDK_WINDOW_HWND (window) == _modal_move_resize_window) goto out; if (with_move && (width < 0 && height < 0)) { gdk_win32_window_move (window, x, y); } else { if (with_move) { gdk_win32_window_move_resize_internal (window, x, y, width, height); } else { gdk_win32_window_resize (window, width, height); } } out: window_impl->inhibit_configure = FALSE; if (WINDOW_IS_TOPLEVEL (window)) _gdk_win32_emit_configure_event (window); } static void gdk_win32_window_raise (GdkWindow *window) { if (!GDK_WINDOW_DESTROYED (window)) { GDK_NOTE (MISC, g_print ("gdk_win32_window_raise: %p\n", GDK_WINDOW_HWND (window))); if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_TEMP) API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)); else if (window->accept_focus) /* Do not wrap this in an API_CALL macro as SetForegroundWindow might * fail when for example dragging a window belonging to a different * application at the time of a gtk_window_present() call due to focus * stealing prevention. */ SetForegroundWindow (GDK_WINDOW_HWND (window)); else API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)); } } static void gdk_win32_window_lower (GdkWindow *window) { if (!GDK_WINDOW_DESTROYED (window)) { GDK_NOTE (MISC, g_print ("gdk_win32_window_lower: %p\n" "... SetWindowPos(%p,HWND_BOTTOM,0,0,0,0," "NOACTIVATE|NOMOVE|NOSIZE)\n", GDK_WINDOW_HWND (window), GDK_WINDOW_HWND (window))); API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)); } } static void gdk_win32_window_set_urgency_hint (GdkWindow *window, gboolean urgent) { FLASHWINFO flashwinfo; typedef BOOL (WINAPI *PFN_FlashWindowEx) (FLASHWINFO*); PFN_FlashWindowEx flashWindowEx = NULL; g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; flashWindowEx = (PFN_FlashWindowEx) GetProcAddress (GetModuleHandle ("user32.dll"), "FlashWindowEx"); if (flashWindowEx) { flashwinfo.cbSize = sizeof (flashwinfo); flashwinfo.hwnd = GDK_WINDOW_HWND (window); if (urgent) flashwinfo.dwFlags = FLASHW_ALL | FLASHW_TIMER; else flashwinfo.dwFlags = FLASHW_STOP; flashwinfo.uCount = 0; flashwinfo.dwTimeout = 0; flashWindowEx (&flashwinfo); } else { FlashWindow (GDK_WINDOW_HWND (window), urgent); } } static gboolean get_effective_window_decorations (GdkWindow *window, GdkWMDecoration *decoration) { GdkWindowImplWin32 *impl; impl = (GdkWindowImplWin32 *)window->impl; if (gdk_window_get_decorations (window, decoration)) return TRUE; if (window->window_type != GDK_WINDOW_TOPLEVEL) { return FALSE; } if ((impl->hint_flags & GDK_HINT_MIN_SIZE) && (impl->hint_flags & GDK_HINT_MAX_SIZE) && impl->hints.min_width == impl->hints.max_width && impl->hints.min_height == impl->hints.max_height) { *decoration = GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MAXIMIZE; if (impl->type_hint == GDK_WINDOW_TYPE_HINT_DIALOG || impl->type_hint == GDK_WINDOW_TYPE_HINT_MENU || impl->type_hint == GDK_WINDOW_TYPE_HINT_TOOLBAR) { *decoration |= GDK_DECOR_MINIMIZE; } else if (impl->type_hint == GDK_WINDOW_TYPE_HINT_SPLASHSCREEN) { *decoration |= GDK_DECOR_MENU | GDK_DECOR_MINIMIZE; } return TRUE; } else if (impl->hint_flags & GDK_HINT_MAX_SIZE) { *decoration = GDK_DECOR_ALL | GDK_DECOR_MAXIMIZE; if (impl->type_hint == GDK_WINDOW_TYPE_HINT_DIALOG || impl->type_hint == GDK_WINDOW_TYPE_HINT_MENU || impl->type_hint == GDK_WINDOW_TYPE_HINT_TOOLBAR) { *decoration |= GDK_DECOR_MINIMIZE; } return TRUE; } else { switch (impl->type_hint) { case GDK_WINDOW_TYPE_HINT_DIALOG: *decoration = (GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); return TRUE; case GDK_WINDOW_TYPE_HINT_MENU: *decoration = (GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); return TRUE; case GDK_WINDOW_TYPE_HINT_TOOLBAR: case GDK_WINDOW_TYPE_HINT_UTILITY: gdk_window_set_skip_taskbar_hint (window, TRUE); gdk_window_set_skip_pager_hint (window, TRUE); *decoration = (GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); return TRUE; case GDK_WINDOW_TYPE_HINT_SPLASHSCREEN: *decoration = (GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MENU | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); return TRUE; case GDK_WINDOW_TYPE_HINT_DOCK: return FALSE; case GDK_WINDOW_TYPE_HINT_DESKTOP: return FALSE; default: /* Fall thru */ case GDK_WINDOW_TYPE_HINT_NORMAL: *decoration = GDK_DECOR_ALL; return TRUE; } } return FALSE; } static void gdk_win32_window_set_geometry_hints (GdkWindow *window, const GdkGeometry *geometry, GdkWindowHints geom_mask) { GdkWindowImplWin32 *impl; FullscreenInfo *fi; g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_window_set_geometry_hints: %p\n", GDK_WINDOW_HWND (window))); impl = GDK_WINDOW_IMPL_WIN32 (window->impl); fi = g_object_get_data (G_OBJECT (window), "fullscreen-info"); if (fi) fi->hint_flags = geom_mask; else impl->hint_flags = geom_mask; impl->hints = *geometry; if (geom_mask & GDK_HINT_POS) { /* even the X11 mplementation doesn't care */ } if (geom_mask & GDK_HINT_MIN_SIZE) { GDK_NOTE (MISC, g_print ("... MIN_SIZE: %dx%d\n", geometry->min_width, geometry->min_height)); } if (geom_mask & GDK_HINT_MAX_SIZE) { GDK_NOTE (MISC, g_print ("... MAX_SIZE: %dx%d\n", geometry->max_width, geometry->max_height)); } if (geom_mask & GDK_HINT_BASE_SIZE) { GDK_NOTE (MISC, g_print ("... BASE_SIZE: %dx%d\n", geometry->base_width, geometry->base_height)); } if (geom_mask & GDK_HINT_RESIZE_INC) { GDK_NOTE (MISC, g_print ("... RESIZE_INC: (%d,%d)\n", geometry->width_inc, geometry->height_inc)); } if (geom_mask & GDK_HINT_ASPECT) { GDK_NOTE (MISC, g_print ("... ASPECT: %g--%g\n", geometry->min_aspect, geometry->max_aspect)); } if (geom_mask & GDK_HINT_WIN_GRAVITY) { GDK_NOTE (MISC, g_print ("... GRAVITY: %d\n", geometry->win_gravity)); } _gdk_win32_window_update_style_bits (window); } static void gdk_win32_window_set_title (GdkWindow *window, const gchar *title) { wchar_t *wtitle; g_return_if_fail (GDK_IS_WINDOW (window)); g_return_if_fail (title != NULL); if (GDK_WINDOW_DESTROYED (window)) return; /* Empty window titles not allowed, so set it to just a period. */ if (!title[0]) title = "."; GDK_NOTE (MISC, g_print ("gdk_window_set_title: %p: %s\n", GDK_WINDOW_HWND (window), title)); GDK_NOTE (MISC_OR_EVENTS, title = g_strdup_printf ("%p %s", GDK_WINDOW_HWND (window), title)); wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL); API_CALL (SetWindowTextW, (GDK_WINDOW_HWND (window), wtitle)); g_free (wtitle); GDK_NOTE (MISC_OR_EVENTS, g_free ((char *) title)); } static void gdk_win32_window_set_role (GdkWindow *window, const gchar *role) { g_return_if_fail (GDK_IS_WINDOW (window)); GDK_NOTE (MISC, g_print ("gdk_window_set_role: %p: %s\n", GDK_WINDOW_HWND (window), (role ? role : "NULL"))); /* XXX */ } static void gdk_win32_window_set_transient_for (GdkWindow *window, GdkWindow *parent) { HWND window_id, parent_id; LONG_PTR old_ptr; DWORD w32_error; GdkWindowImplWin32 *window_impl = GDK_WINDOW_IMPL_WIN32 (window->impl); GdkWindowImplWin32 *parent_impl = NULL; GSList *item; g_return_if_fail (GDK_IS_WINDOW (window)); window_id = GDK_WINDOW_HWND (window); parent_id = parent != NULL ? GDK_WINDOW_HWND (parent) : NULL; GDK_NOTE (MISC, g_print ("gdk_window_set_transient_for: %p: %p\n", window_id, parent_id)); if (GDK_WINDOW_DESTROYED (window) || (parent && GDK_WINDOW_DESTROYED (parent))) { if (GDK_WINDOW_DESTROYED (window)) GDK_NOTE (MISC, g_print ("... destroyed!\n")); else GDK_NOTE (MISC, g_print ("... owner destroyed!\n")); return; } if (parent == NULL) { GdkWindowImplWin32 *trans_impl = GDK_WINDOW_IMPL_WIN32 (window_impl->transient_owner->impl); if (trans_impl->transient_children != NULL) { item = g_slist_find (trans_impl->transient_children, window); item->data = NULL; trans_impl->transient_children = g_slist_delete_link (trans_impl->transient_children, item); trans_impl->num_transients--; if (!trans_impl->num_transients) { trans_impl->transient_children = NULL; } } g_object_unref (G_OBJECT (window_impl->transient_owner)); g_object_unref (G_OBJECT (window)); window_impl->transient_owner = NULL; } else { parent_impl = GDK_WINDOW_IMPL_WIN32 (parent->impl); parent_impl->transient_children = g_slist_append (parent_impl->transient_children, window); g_object_ref (G_OBJECT (window)); parent_impl->num_transients++; window_impl->transient_owner = parent; g_object_ref (G_OBJECT (parent)); } SetLastError (0); old_ptr = GetWindowLongPtr (window_id, GWLP_HWNDPARENT); w32_error = GetLastError (); /* Don't re-set GWLP_HWNDPARENT to the same value */ if ((HWND) old_ptr == parent_id && w32_error == NO_ERROR) return; /* Don't return if it failed, try SetWindowLongPtr() anyway */ if (old_ptr == 0 && w32_error != NO_ERROR) WIN32_API_FAILED ("GetWindowLongPtr"); /* This changes the *owner* of the window, despite the misleading * name. (Owner and parent are unrelated concepts.) At least that's * what people who seem to know what they talk about say on * USENET. Search on Google. */ SetLastError (0); old_ptr = SetWindowLongPtr (window_id, GWLP_HWNDPARENT, (LONG_PTR) parent_id); w32_error = GetLastError (); if (old_ptr == 0 && w32_error != NO_ERROR) WIN32_API_FAILED ("SetWindowLongPtr"); } void _gdk_push_modal_window (GdkWindow *window) { modal_window_stack = g_slist_prepend (modal_window_stack, window); } void _gdk_remove_modal_window (GdkWindow *window) { GSList *tmp; g_return_if_fail (window != NULL); /* It's possible to be NULL here if someone sets the modal hint of the window * to FALSE before a modal window stack has ever been created. */ if (modal_window_stack == NULL) return; /* Find the requested window in the stack and remove it. Yeah, I realize this * means we're not a 'real stack', strictly speaking. Sue me. :) */ tmp = g_slist_find (modal_window_stack, window); if (tmp != NULL) { modal_window_stack = g_slist_delete_link (modal_window_stack, tmp); } } gboolean _gdk_modal_blocked (GdkWindow *window) { GSList *l; gboolean found_any = FALSE; for (l = modal_window_stack; l != NULL; l = l->next) { GdkWindow *modal = l->data; if (modal == window) return FALSE; if (GDK_WINDOW_IS_MAPPED (modal)) found_any = TRUE; } return found_any; } GdkWindow * _gdk_modal_current (void) { GSList *l; for (l = modal_window_stack; l != NULL; l = l->next) { GdkWindow *modal = l->data; if (GDK_WINDOW_IS_MAPPED (modal)) return modal; } return NULL; } static void gdk_win32_window_get_geometry (GdkWindow *window, gint *x, gint *y, gint *width, gint *height) { GdkDisplay *display; gboolean window_is_root; display = gdk_window_get_display (window); if (!GDK_WINDOW_DESTROYED (window)) { RECT rect; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); API_CALL (GetClientRect, (GDK_WINDOW_HWND (window), &rect)); POINT pt; GdkWindow *parent = gdk_window_get_parent (window); pt.x = rect.left; pt.y = rect.top; ClientToScreen (GDK_WINDOW_HWND (window), &pt); if (parent) ScreenToClient (GDK_WINDOW_HWND (parent), &pt); rect.left = pt.x; rect.top = pt.y; pt.x = rect.right; pt.y = rect.bottom; ClientToScreen (GDK_WINDOW_HWND (window), &pt); if (parent) ScreenToClient (GDK_WINDOW_HWND (parent), &pt); rect.right = pt.x; rect.bottom = pt.y; if (parent == NULL) { rect.left += _gdk_offset_x * impl->window_scale; rect.top += _gdk_offset_y * impl->window_scale; rect.right += _gdk_offset_x * impl->window_scale; rect.bottom += _gdk_offset_y * impl->window_scale; } if (x) *x = rect.left / impl->window_scale; if (y) *y = rect.top / impl->window_scale; if (width) *width = (rect.right - rect.left) / impl->window_scale; if (height) *height = (rect.bottom - rect.top) / impl->window_scale; GDK_NOTE (MISC, g_print ("gdk_win32_window_get_geometry: %p: %ldx%ld@%+ld%\n", GDK_WINDOW_HWND (window), (rect.right - rect.left) / impl->window_scale, (rect.bottom - rect.top) / impl->window_scale, rect.left, rect.top)); } } static void gdk_win32_window_get_root_coords (GdkWindow *window, gint x, gint y, gint *root_x, gint *root_y) { gint tx; gint ty; POINT pt; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); pt.x = x * impl->window_scale; pt.y = y * impl->window_scale; ClientToScreen (GDK_WINDOW_HWND (window), &pt); tx = pt.x; ty = pt.y; if (root_x) *root_x = (tx + _gdk_offset_x) / impl->window_scale; if (root_y) *root_y = (ty + _gdk_offset_y) / impl->window_scale; GDK_NOTE (MISC, g_print ("gdk_win32_window_get_root_coords: %p: %+d%+d %+d%+d\n", GDK_WINDOW_HWND (window), x * impl->window_scale, y * impl->window_scale, (tx + _gdk_offset_x) / impl->window_scale, (ty + _gdk_offset_y) / impl->window_scale)); } static void gdk_win32_window_restack_toplevel (GdkWindow *window, GdkWindow *sibling, gboolean above) { // ### TODO } static void gdk_win32_window_get_frame_extents (GdkWindow *window, GdkRectangle *rect) { HWND hwnd; RECT r; GdkWindowImplWin32 *impl; g_return_if_fail (GDK_IS_WINDOW (window)); g_return_if_fail (rect != NULL); rect->x = 0; rect->y = 0; rect->width = 1; rect->height = 1; if (GDK_WINDOW_DESTROYED (window)) return; /* FIXME: window is documented to be a toplevel GdkWindow, so is it really * necessary to walk its parent chain? */ while (window->parent && window->parent->parent) window = window->parent; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); hwnd = GDK_WINDOW_HWND (window); API_CALL (GetWindowRect, (hwnd, &r)); /* Initialize to real, unscaled size */ rect->x = r.left + _gdk_offset_x * impl->window_scale; rect->y = r.top + _gdk_offset_y * impl->window_scale; rect->width = (r.right - r.left); rect->height = (r.bottom - r.top); /* Extend width and height to ensure that they cover the real size when de-scaled, * and replace everyting with scaled values */ rect->width = (rect->width + rect->x % impl->window_scale + impl->window_scale - 1) / impl->window_scale; rect->height = (rect->height + rect->y % impl->window_scale + impl->window_scale - 1) / impl->window_scale; rect->x = r.left / impl->window_scale + _gdk_offset_x; rect->y = r.top / impl->window_scale + _gdk_offset_y; GDK_NOTE (MISC, g_print ("gdk_window_get_frame_extents: %p: %ldx%ld@%+ld%+ld\n", GDK_WINDOW_HWND (window), rect->width, rect->height, rect->x, rect->y)); } static gboolean gdk_window_win32_get_device_state (GdkWindow *window, GdkDevice *device, gdouble *x, gdouble *y, GdkModifierType *mask) { GdkWindow *child; g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), FALSE); GDK_DEVICE_GET_CLASS (device)->query_state (device, window, &child, NULL, NULL, x, y, mask); return (child != NULL); } void gdk_display_warp_device (GdkDisplay *display, GdkDevice *device, gint x, gint y) { g_return_if_fail (display == gdk_display_get_default ()); g_return_if_fail (GDK_IS_DEVICE (device)); g_return_if_fail (display == gdk_device_get_display (device)); GDK_DEVICE_GET_CLASS (device)->warp (device, x, y); } static GdkEventMask gdk_win32_window_get_events (GdkWindow *window) { GdkWindowImplWin32 *impl; if (GDK_WINDOW_DESTROYED (window)) return 0; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); return impl->native_event_mask; } static void gdk_win32_window_set_events (GdkWindow *window, GdkEventMask event_mask) { GdkWindowImplWin32 *impl; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); /* gdk_window_new() always sets the GDK_STRUCTURE_MASK, so better * set it here, too. Not that I know or remember why it is * necessary, will have to test some day. */ impl->native_event_mask = GDK_STRUCTURE_MASK | event_mask; } static void do_shape_combine_region (GdkWindow *window, HRGN hrgn, gint x, gint y) { RECT rect; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); GetClientRect (GDK_WINDOW_HWND (window), &rect); _gdk_win32_adjust_client_rect (window, &rect); OffsetRgn (hrgn, -rect.left, -rect.top); OffsetRgn (hrgn, x, y); /* If this is a top-level window, add the title bar to the region */ if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_TOPLEVEL) { HRGN tmp = CreateRectRgn (0, 0, rect.right - rect.left, -rect.top); CombineRgn (hrgn, hrgn, tmp, RGN_OR); DeleteObject (tmp); } SetWindowRgn (GDK_WINDOW_HWND (window), hrgn, TRUE); } static void gdk_win32_window_set_accept_focus (GdkWindow *window, gboolean accept_focus) { g_return_if_fail (GDK_IS_WINDOW (window)); accept_focus = accept_focus != FALSE; if (window->accept_focus != accept_focus) window->accept_focus = accept_focus; } static void gdk_win32_window_set_focus_on_map (GdkWindow *window, gboolean focus_on_map) { g_return_if_fail (GDK_IS_WINDOW (window)); focus_on_map = focus_on_map != FALSE; if (window->focus_on_map != focus_on_map) window->focus_on_map = focus_on_map; } static void gdk_win32_window_set_icon_list (GdkWindow *window, GList *textures) { GdkTexture *big_texture, *small_texture; gint big_diff, small_diff; gint big_w, big_h, small_w, small_h; gint w, h; gint dw, dh, diff; HICON small_hicon, big_hicon; GdkWindowImplWin32 *impl; g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window) || textures == NULL) return; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); /* ideal sizes for small and large icons */ big_w = GetSystemMetrics (SM_CXICON); big_h = GetSystemMetrics (SM_CYICON); small_w = GetSystemMetrics (SM_CXSMICON); small_h = GetSystemMetrics (SM_CYSMICON); /* find closest sized icons in the list */ big_texture = NULL; small_texture = NULL; big_diff = 0; small_diff = 0; for (GList *l = textures; l; l = l->next) { GdkTexture *texture = l->data; w = gdk_texture_get_width (texture); h = gdk_texture_get_height (texture); dw = ABS (w - big_w); dh = ABS (h - big_h); diff = dw*dw + dh*dh; if (big_texture == NULL || diff < big_diff) { big_texture = texture; big_diff = diff; } dw = ABS (w - small_w); dh = ABS (h - small_h); diff = dw*dw + dh*dh; if (small_texture == NULL || diff < small_diff) { small_texture = texture; small_diff = diff; } textures = textures->next; } /* Create the icons */ big_hicon = _gdk_win32_texture_to_hicon (big_texture); g_object_unref (big_texture); small_hicon = _gdk_win32_texture_to_hicon (small_texture); g_object_unref (small_texture); /* Set the icons */ SendMessageW (GDK_WINDOW_HWND (window), WM_SETICON, ICON_BIG, (LPARAM)big_hicon); SendMessageW (GDK_WINDOW_HWND (window), WM_SETICON, ICON_SMALL, (LPARAM)small_hicon); /* Store the icons, destroying any previous icons */ if (impl->hicon_big) GDI_CALL (DestroyIcon, (impl->hicon_big)); impl->hicon_big = big_hicon; if (impl->hicon_small) GDI_CALL (DestroyIcon, (impl->hicon_small)); impl->hicon_small = small_hicon; } static void gdk_win32_window_set_icon_name (GdkWindow *window, const gchar *name) { /* In case I manage to confuse this again (or somebody else does): * Please note that "icon name" here really *does* mean the name or * title of an window minimized as an icon on the desktop, or in the * taskbar. It has nothing to do with the freedesktop.org icon * naming stuff. */ g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; #if 0 /* This is not the correct thing to do. We should keep both the * "normal" window title, and the icon name. When the window is * minimized, call SetWindowText() with the icon name, and when the * window is restored, with the normal window title. Also, the name * is in UTF-8, so we should do the normal conversion to either wide * chars or system codepage, and use either the W or A version of * SetWindowText(), depending on Windows version. */ API_CALL (SetWindowText, (GDK_WINDOW_HWND (window), name)); #endif } static GdkWindow * gdk_win32_window_get_group (GdkWindow *window) { g_return_val_if_fail (GDK_IS_WINDOW (window), NULL); if (GDK_WINDOW_DESTROYED (window)) return NULL; g_warning ("gdk_window_get_group not yet implemented"); return NULL; } static void gdk_win32_window_set_group (GdkWindow *window, GdkWindow *leader) { g_return_if_fail (GDK_IS_WINDOW (window)); g_return_if_fail (leader == NULL || GDK_IS_WINDOW (leader)); if (GDK_WINDOW_DESTROYED (window) || GDK_WINDOW_DESTROYED (leader)) return; g_warning ("gdk_window_set_group not implemented"); } static void update_single_bit (LONG *style, gboolean all, int gdk_bit, int style_bit) { /* all controls the interpretation of gdk_bit -- if all is TRUE, * gdk_bit indicates whether style_bit is off; if all is FALSE, gdk * bit indicate whether style_bit is on */ if ((!all && gdk_bit) || (all && !gdk_bit)) *style |= style_bit; else *style &= ~style_bit; } /* * Returns TRUE if window has no decorations. * Usually it means CSD windows, because GTK * calls gdk_window_set_decorations (window, 0); * This is used to decide whether a toplevel should * be made layered, thus it * only returns TRUE for toplevels (until GTK minimal * system requirements are lifted to Windows 8 or newer, * because only toplevels can be layered). */ gboolean _gdk_win32_window_lacks_wm_decorations (GdkWindow *window) { GdkWindowImplWin32 *impl; LONG style; gboolean has_any_decorations; if (GDK_WINDOW_DESTROYED (window)) return FALSE; /* only toplevels can be layered */ if (!WINDOW_IS_TOPLEVEL (window)) return FALSE; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); /* This is because GTK calls gdk_window_set_decorations (window, 0), * even though GdkWMDecoration docs indicate that 0 does NOT mean * "no decorations". */ if (impl->decorations && *impl->decorations == 0) return TRUE; if (GDK_WINDOW_HWND (window) == 0) return FALSE; style = GetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE); if (style == 0) { DWORD w32_error = GetLastError (); GDK_NOTE (MISC, g_print ("Failed to get style of window %p (handle %p): %lu\n", window, GDK_WINDOW_HWND (window), w32_error)); return FALSE; } /* Keep this in sync with _gdk_win32_window_update_style_bits() */ /* We don't check what get_effective_window_decorations() * has to say, because it gives suggestions based on * various hints, while we want *actual* decorations, * or their absence. */ has_any_decorations = FALSE; if (style & (WS_BORDER | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)) has_any_decorations = TRUE; else GDK_NOTE (MISC, g_print ("Window %p (handle %p): has no decorations (style %lx)\n", window, GDK_WINDOW_HWND (window), style)); return !has_any_decorations; } void _gdk_win32_window_update_style_bits (GdkWindow *window) { GdkWindowImplWin32 *impl = (GdkWindowImplWin32 *)window->impl; GdkWMDecoration decorations; LONG old_style, new_style, old_exstyle, new_exstyle; gboolean all; RECT rect, before, after; gboolean was_topmost; gboolean will_be_topmost; HWND insert_after; UINT flags; if (window->state & GDK_WINDOW_STATE_FULLSCREEN) return; old_style = GetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE); old_exstyle = GetWindowLong (GDK_WINDOW_HWND (window), GWL_EXSTYLE); GetClientRect (GDK_WINDOW_HWND (window), &before); after = before; AdjustWindowRectEx (&before, old_style, FALSE, old_exstyle); was_topmost = (old_exstyle & WS_EX_TOPMOST) ? TRUE : FALSE; will_be_topmost = was_topmost; old_exstyle &= ~WS_EX_TOPMOST; new_style = old_style; new_exstyle = old_exstyle; if (window->window_type == GDK_WINDOW_TEMP) { new_exstyle |= WS_EX_TOOLWINDOW; will_be_topmost = TRUE; } else if (impl->type_hint == GDK_WINDOW_TYPE_HINT_UTILITY) { new_exstyle |= WS_EX_TOOLWINDOW; } else { new_exstyle &= ~WS_EX_TOOLWINDOW; } /* We can get away with using layered windows * only when no decorations are needed. It can mean * CSD or borderless non-CSD windows (tooltips?). * * If this window cannot use layered windows, disable it always. * This currently applies to windows using OpenGL, which * does not work with layered windows. */ if (impl->suppress_layered == 0) { if (_gdk_win32_window_lacks_wm_decorations (window)) impl->layered = g_strcmp0 (g_getenv ("GDK_WIN32_LAYERED"), "0") != 0; } else impl->layered = FALSE; if (impl->layered) new_exstyle |= WS_EX_LAYERED; else new_exstyle &= ~WS_EX_LAYERED; if (get_effective_window_decorations (window, &decorations)) { all = (decorations & GDK_DECOR_ALL); /* Keep this in sync with the test in _gdk_win32_window_lacks_wm_decorations() */ update_single_bit (&new_style, all, decorations & GDK_DECOR_BORDER, WS_BORDER); update_single_bit (&new_style, all, decorations & GDK_DECOR_RESIZEH, WS_THICKFRAME); update_single_bit (&new_style, all, decorations & GDK_DECOR_TITLE, WS_CAPTION); update_single_bit (&new_style, all, decorations & GDK_DECOR_MENU, WS_SYSMENU); update_single_bit (&new_style, all, decorations & GDK_DECOR_MINIMIZE, WS_MINIMIZEBOX); update_single_bit (&new_style, all, decorations & GDK_DECOR_MAXIMIZE, WS_MAXIMIZEBOX); } if (old_style == new_style && old_exstyle == new_exstyle ) { GDK_NOTE (MISC, g_print ("_gdk_win32_window_update_style_bits: %p: no change\n", GDK_WINDOW_HWND (window))); return; } if (old_style != new_style) { GDK_NOTE (MISC, g_print ("_gdk_win32_window_update_style_bits: %p: STYLE: %s => %s\n", GDK_WINDOW_HWND (window), _gdk_win32_window_style_to_string (old_style), _gdk_win32_window_style_to_string (new_style))); SetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE, new_style); } if (old_exstyle != new_exstyle) { GDK_NOTE (MISC, g_print ("_gdk_win32_window_update_style_bits: %p: EXSTYLE: %s => %s\n", GDK_WINDOW_HWND (window), _gdk_win32_window_exstyle_to_string (old_exstyle), _gdk_win32_window_exstyle_to_string (new_exstyle))); SetWindowLong (GDK_WINDOW_HWND (window), GWL_EXSTYLE, new_exstyle); } AdjustWindowRectEx (&after, new_style, FALSE, new_exstyle); GetWindowRect (GDK_WINDOW_HWND (window), &rect); rect.left += after.left - before.left; rect.top += after.top - before.top; rect.right += after.right - before.right; rect.bottom += after.bottom - before.bottom; flags = SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOREPOSITION; if (will_be_topmost && !was_topmost) { insert_after = HWND_TOPMOST; } else if (was_topmost && !will_be_topmost) { insert_after = HWND_NOTOPMOST; } else { flags |= SWP_NOZORDER; insert_after = SWP_NOZORDER_SPECIFIED; } SetWindowPos (GDK_WINDOW_HWND (window), insert_after, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags); } static void update_single_system_menu_entry (HMENU hmenu, gboolean all, int gdk_bit, int menu_entry) { /* all controls the interpretation of gdk_bit -- if all is TRUE, * gdk_bit indicates whether menu entry is disabled; if all is * FALSE, gdk bit indicate whether menu entry is enabled */ if ((!all && gdk_bit) || (all && !gdk_bit)) EnableMenuItem (hmenu, menu_entry, MF_BYCOMMAND | MF_ENABLED); else EnableMenuItem (hmenu, menu_entry, MF_BYCOMMAND | MF_GRAYED); } static void update_system_menu (GdkWindow *window) { GdkWMFunction functions; BOOL all; if (_gdk_window_get_functions (window, &functions)) { HMENU hmenu = GetSystemMenu (GDK_WINDOW_HWND (window), FALSE); all = (functions & GDK_FUNC_ALL); update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_RESIZE, SC_SIZE); update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_MOVE, SC_MOVE); update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_MINIMIZE, SC_MINIMIZE); update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_MAXIMIZE, SC_MAXIMIZE); update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_CLOSE, SC_CLOSE); } } static void gdk_win32_window_set_decorations (GdkWindow *window, GdkWMDecoration decorations) { GdkWindowImplWin32 *impl; g_return_if_fail (GDK_IS_WINDOW (window)); impl = GDK_WINDOW_IMPL_WIN32 (window->impl); GDK_NOTE (MISC, g_print ("gdk_window_set_decorations: %p: %s %s%s%s%s%s%s\n", GDK_WINDOW_HWND (window), (decorations & GDK_DECOR_ALL ? "clearing" : "setting"), (decorations & GDK_DECOR_BORDER ? "BORDER " : ""), (decorations & GDK_DECOR_RESIZEH ? "RESIZEH " : ""), (decorations & GDK_DECOR_TITLE ? "TITLE " : ""), (decorations & GDK_DECOR_MENU ? "MENU " : ""), (decorations & GDK_DECOR_MINIMIZE ? "MINIMIZE " : ""), (decorations & GDK_DECOR_MAXIMIZE ? "MAXIMIZE " : ""))); if (!impl->decorations) impl->decorations = g_malloc (sizeof (GdkWMDecoration)); *impl->decorations = decorations; _gdk_win32_window_update_style_bits (window); } static gboolean gdk_win32_window_get_decorations (GdkWindow *window, GdkWMDecoration *decorations) { GdkWindowImplWin32 *impl; g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE); impl = GDK_WINDOW_IMPL_WIN32 (window->impl); if (impl->decorations == NULL) return FALSE; *decorations = *impl->decorations; return TRUE; } static GQuark get_functions_quark () { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("gdk-window-functions"); return quark; } static void gdk_win32_window_set_functions (GdkWindow *window, GdkWMFunction functions) { GdkWMFunction* functions_copy; g_return_if_fail (GDK_IS_WINDOW (window)); GDK_NOTE (MISC, g_print ("gdk_window_set_functions: %p: %s %s%s%s%s%s\n", GDK_WINDOW_HWND (window), (functions & GDK_FUNC_ALL ? "clearing" : "setting"), (functions & GDK_FUNC_RESIZE ? "RESIZE " : ""), (functions & GDK_FUNC_MOVE ? "MOVE " : ""), (functions & GDK_FUNC_MINIMIZE ? "MINIMIZE " : ""), (functions & GDK_FUNC_MAXIMIZE ? "MAXIMIZE " : ""), (functions & GDK_FUNC_CLOSE ? "CLOSE " : ""))); functions_copy = g_malloc (sizeof (GdkWMFunction)); *functions_copy = functions; g_object_set_qdata_full (G_OBJECT (window), get_functions_quark (), functions_copy, g_free); update_system_menu (window); } gboolean _gdk_window_get_functions (GdkWindow *window, GdkWMFunction *functions) { GdkWMFunction* functions_set; functions_set = g_object_get_qdata (G_OBJECT (window), get_functions_quark ()); if (functions_set) *functions = *functions_set; return (functions_set != NULL); } #if defined(MORE_AEROSNAP_DEBUGGING) static void log_region (gchar *prefix, AeroSnapEdgeRegion *region) { GDK_NOTE (MISC, g_print ("Region %s:\n" "edge %d x %d @ %d x %d\n" "trig %d x %d @ %d x %d\n", prefix, region->edge.width, region->edge.height, region->edge.x, region->edge.y, region->trigger.width, region->trigger.height, region->trigger.x, region->trigger.y)); } #endif static void calculate_aerosnap_regions (GdkW32DragMoveResizeContext *context) { GdkDisplay *display; gint n_monitors, monitor_idx, other_monitor_idx; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (context->window->impl); #if defined(MORE_AEROSNAP_DEBUGGING) gint i; #endif display = gdk_display_get_default (); n_monitors = gdk_display_get_n_monitors (display); #define _M_UP 0 #define _M_DOWN 1 #define _M_LEFT 2 #define _M_RIGHT 3 for (monitor_idx = 0; monitor_idx < n_monitors; monitor_idx++) { GdkRectangle wa; GdkRectangle geometry; AeroSnapEdgeRegion snap_region; gboolean move_edge[4] = { TRUE, FALSE, TRUE, TRUE }; gboolean resize_edge[2] = { TRUE, TRUE }; gint diff; gint thickness, trigger_thickness; GdkMonitor *monitor; monitor = gdk_display_get_monitor (display, monitor_idx); gdk_monitor_get_workarea (monitor, &wa); gdk_monitor_get_geometry (monitor, &geometry); for (other_monitor_idx = 0; other_monitor_idx < n_monitors && (move_edge[_M_UP] || move_edge[_M_LEFT] || move_edge[_M_RIGHT] || resize_edge[_M_DOWN]); other_monitor_idx++) { GdkRectangle other_wa; GdkMonitor *other_monitor; if (other_monitor_idx == monitor_idx) continue; other_monitor = gdk_display_get_monitor (display, other_monitor_idx); gdk_monitor_get_workarea (other_monitor, &other_wa); /* An edge triggers AeroSnap only if there are no * monitors beyond that edge. * Even if there's another monitor, but it does not cover * the whole edge (it's smaller or is not aligned to * the corner of current monitor), that edge is still * removed from the trigger list. */ if (other_wa.x >= wa.x + wa.width) move_edge[_M_RIGHT] = FALSE; if (other_wa.x + other_wa.width <= wa.x) move_edge[_M_LEFT] = FALSE; if (other_wa.y + other_wa.height <= wa.y) { move_edge[_M_UP] = FALSE; resize_edge[_M_UP] = FALSE; } if (other_wa.y >= wa.y + wa.height) { /* no move_edge for the bottom edge, just resize_edge */ resize_edge[_M_DOWN] = FALSE; } } thickness = AEROSNAP_REGION_THICKNESS * impl->window_scale; trigger_thickness = AEROSNAP_REGION_TRIGGER_THICKNESS * impl->window_scale; snap_region.edge = wa; snap_region.trigger = wa; snap_region.edge.height = thickness; snap_region.trigger.height = trigger_thickness; /* Extend both regions into toolbar space. * When there's no toolbar, diff == 0. */ diff = wa.y - geometry.y; snap_region.edge.height += diff; snap_region.edge.y -= diff; snap_region.trigger.height += diff; snap_region.trigger.y -= diff; if (move_edge[_M_UP]) g_array_append_val (context->maximize_regions, snap_region); if (resize_edge[_M_UP]) g_array_append_val (context->fullup_regions, snap_region); snap_region.edge = wa; snap_region.trigger = wa; snap_region.edge.width = thickness; snap_region.trigger.width = trigger_thickness; diff = wa.x - geometry.x; snap_region.edge.width += diff; snap_region.edge.x -= diff; snap_region.trigger.width += diff; snap_region.trigger.x -= diff; if (move_edge[_M_LEFT]) g_array_append_val (context->halfleft_regions, snap_region); snap_region.edge = wa; snap_region.trigger = wa; snap_region.edge.x += wa.width - thickness; snap_region.edge.width = thickness; snap_region.trigger.x += wa.width - trigger_thickness; snap_region.trigger.width = trigger_thickness; diff = (geometry.x + geometry.width) - (wa.x + wa.width); snap_region.edge.width += diff; snap_region.trigger.width += diff; if (move_edge[_M_RIGHT]) g_array_append_val (context->halfright_regions, snap_region); snap_region.edge = wa; snap_region.trigger = wa; snap_region.edge.y += wa.height - thickness; snap_region.edge.height = thickness; snap_region.trigger.y += wa.height - trigger_thickness; snap_region.trigger.height = trigger_thickness; diff = (geometry.y + geometry.height) - (wa.y + wa.height); snap_region.edge.height += diff; snap_region.trigger.height += diff; if (resize_edge[_M_DOWN]) g_array_append_val (context->fullup_regions, snap_region); } #undef _M_UP #undef _M_DOWN #undef _M_LEFT #undef _M_RIGHT #if defined(MORE_AEROSNAP_DEBUGGING) for (i = 0; i < context->maximize_regions->len; i++) log_region ("maximize", &g_array_index (context->maximize_regions, AeroSnapEdgeRegion, i)); for (i = 0; i < context->halfleft_regions->len; i++) log_region ("halfleft", &g_array_index (context->halfleft_regions, AeroSnapEdgeRegion, i)); for (i = 0; i < context->halfright_regions->len; i++) log_region ("halfright", &g_array_index (context->halfright_regions, AeroSnapEdgeRegion, i)); for (i = 0; i < context->fullup_regions->len; i++) log_region ("fullup", &g_array_index (context->fullup_regions, AeroSnapEdgeRegion, i)); #endif } static void discard_snapinfo (GdkWindow *window) { GdkWindowImplWin32 *impl; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); impl->snap_state = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; if (impl->snap_stash == NULL) return; g_clear_pointer (&impl->snap_stash, g_free); g_clear_pointer (&impl->snap_stash_int, g_free); } static void unsnap (GdkWindow *window, GdkMonitor *monitor) { GdkWindowImplWin32 *impl; GdkRectangle rect; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); impl->snap_state = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; if (impl->snap_stash == NULL) return; gdk_monitor_get_workarea (monitor, &rect); GDK_NOTE (MISC, g_print ("Monitor work area %d x %d @ %d : %d\n", rect.width, rect.height, rect.x, rect.y)); if (rect.width >= impl->snap_stash_int->width && rect.height >= impl->snap_stash_int->height) { /* If the window fits into new work area without resizing it, * place it into new work area without resizing it. */ gdouble left, right, up, down, hratio, vratio; gdouble hscale, vscale; gdouble new_left, new_up; left = impl->snap_stash->x; right = 1.0 - (impl->snap_stash->x + impl->snap_stash->width); up = impl->snap_stash->y; down = 1.0 - (impl->snap_stash->y + impl->snap_stash->height); hscale = 1.0; if (right > 0.001) { hratio = left / right; hscale = hratio / (1.0 + hratio); } new_left = (gdouble) (rect.width - impl->snap_stash_int->width) * hscale; vscale = 1.0; if (down > 0.001) { vratio = up / down; vscale = vratio / (1.0 + vratio); } new_up = (gdouble) (rect.height - impl->snap_stash_int->height) * vscale; rect.x = round (rect.x + new_left); rect.y = round (rect.y + new_up); rect.width = impl->snap_stash_int->width; rect.height = impl->snap_stash_int->height; } else { /* Calculate actual unsnapped window size based on its * old relative size. Same for position. */ rect.x += round (rect.width * impl->snap_stash->x); rect.y += round (rect.height * impl->snap_stash->y); rect.width = round (rect.width * impl->snap_stash->width); rect.height = round (rect.height * impl->snap_stash->height); } GDK_NOTE (MISC, g_print ("Unsnapped window size %d x %d @ %d : %d\n", rect.width, rect.height, rect.x, rect.y)); gdk_window_move_resize (window, rect.x, rect.y, rect.width, rect.height); g_clear_pointer (&impl->snap_stash, g_free); g_clear_pointer (&impl->snap_stash_int, g_free); } static void stash_window (GdkWindow *window, GdkWindowImplWin32 *impl) { gint x, y; gint width, wwidth; gint height, wheight; WINDOWPLACEMENT placement; HMONITOR hmonitor; MONITORINFO hmonitor_info; placement.length = sizeof(WINDOWPLACEMENT); /* Use W32 API to get unmaximized window size, which GDK doesn't remember */ if (!GetWindowPlacement (GDK_WINDOW_HWND (window), &placement)) return; /* MSDN is very vague, but in practice rcNormalPosition is the same as GetWindowRect(), * only with adjustments for toolbars (which creates rather weird coodinate space issues). * We need to get monitor info and apply workarea vs monitorarea diff to turn * these into screen coordinates proper. */ hmonitor = MonitorFromWindow (GDK_WINDOW_HWND (window), MONITOR_DEFAULTTONEAREST); hmonitor_info.cbSize = sizeof (hmonitor_info); if (!GetMonitorInfoA (hmonitor, &hmonitor_info)) return; if (impl->snap_stash == NULL) impl->snap_stash = g_new0 (GdkRectangleDouble, 1); if (impl->snap_stash_int == NULL) impl->snap_stash_int = g_new0 (GdkRectangle, 1); GDK_NOTE (MISC, g_print ("monitor work area %ld x %ld @ %ld : %ld\n", (hmonitor_info.rcWork.right - hmonitor_info.rcWork.left) / impl->window_scale, (hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top) / impl->window_scale, hmonitor_info.rcWork.left, hmonitor_info.rcWork.top)); GDK_NOTE (MISC, g_print ("monitor area %ld x %ld @ %ld : %ld\n", (hmonitor_info.rcMonitor.right - hmonitor_info.rcMonitor.left) / impl->window_scale, (hmonitor_info.rcMonitor.bottom - hmonitor_info.rcMonitor.top) / impl->window_scale, hmonitor_info.rcMonitor.left, hmonitor_info.rcMonitor.top)); GDK_NOTE (MISC, g_print ("window work place %ld x %ld @ %ld : %ld\n", (placement.rcNormalPosition.right - placement.rcNormalPosition.left) / impl->window_scale, (placement.rcNormalPosition.bottom - placement.rcNormalPosition.top) / impl->window_scale, placement.rcNormalPosition.left, placement.rcNormalPosition.top)); width = (placement.rcNormalPosition.right - placement.rcNormalPosition.left) / impl->window_scale; height = (placement.rcNormalPosition.bottom - placement.rcNormalPosition.top) / impl->window_scale; x = (placement.rcNormalPosition.left - hmonitor_info.rcMonitor.left) / impl->window_scale; y = (placement.rcNormalPosition.top - hmonitor_info.rcMonitor.top) / impl->window_scale; wwidth = (hmonitor_info.rcWork.right - hmonitor_info.rcWork.left) / impl->window_scale; wheight = (hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top) / impl->window_scale; impl->snap_stash->x = (gdouble) (x) / (gdouble) (wwidth); impl->snap_stash->y = (gdouble) (y) / (gdouble) (wheight); impl->snap_stash->width = (gdouble) width / (gdouble) (wwidth); impl->snap_stash->height = (gdouble) height / (gdouble) (wheight); impl->snap_stash_int->x = x; impl->snap_stash_int->y = y; impl->snap_stash_int->width = width; impl->snap_stash_int->height = height; GDK_NOTE (MISC, g_print ("Stashed window %d x %d @ %d : %d as %f x %f @ %f : %f\n", width, height, x, y, impl->snap_stash->width, impl->snap_stash->height, impl->snap_stash->x, impl->snap_stash->y)); } static void snap_up (GdkWindow *window) { SHORT maxysize; gint x, y; gint width, height; GdkWindowImplWin32 *impl; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); impl->snap_state = GDK_WIN32_AEROSNAP_STATE_FULLUP; stash_window (window, impl); maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN) / impl->window_scale; gdk_window_get_position (window, &x, &y); width = gdk_window_get_width (window); y = 0; height = maxysize; x = x - impl->margins.left; y = y - impl->margins.top; width += impl->margins_x; height += impl->margins_y; gdk_window_move_resize (window, x, y, width, height); } static void snap_left (GdkWindow *window, GdkMonitor *monitor, GdkMonitor *snap_monitor) { GdkRectangle rect; GdkWindowImplWin32 *impl; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); impl->snap_state = GDK_WIN32_AEROSNAP_STATE_HALFLEFT; gdk_monitor_get_workarea (snap_monitor, &rect); stash_window (window, impl); rect.width = rect.width / 2; rect.x = rect.x - impl->margins.left; rect.y = rect.y - impl->margins.top; rect.width = rect.width + impl->margins_x; rect.height = rect.height + impl->margins_y; gdk_window_move_resize (window, rect.x, rect.y, rect.width, rect.height); } static void snap_right (GdkWindow *window, GdkMonitor *monitor, GdkMonitor *snap_monitor) { GdkRectangle rect; GdkWindowImplWin32 *impl; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); impl->snap_state = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT; gdk_monitor_get_workarea (snap_monitor, &rect); stash_window (window, impl); rect.width = rect.width / 2; rect.x += rect.width; rect.x = rect.x - impl->margins.left; rect.y = rect.y - impl->margins.top; rect.width = rect.width + impl->margins_x; rect.height = rect.height + impl->margins_y; gdk_window_move_resize (window, rect.x, rect.y, rect.width, rect.height); } void _gdk_win32_window_handle_aerosnap (GdkWindow *window, GdkWin32AeroSnapCombo combo) { GdkWindowImplWin32 *impl; GdkDisplay *display; gint n_monitors; GdkWindowState window_state = gdk_window_get_state (window); gboolean minimized = window_state & GDK_WINDOW_STATE_ICONIFIED; gboolean maximized = window_state & GDK_WINDOW_STATE_MAXIMIZED; gboolean halfsnapped; GdkMonitor *monitor; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); display = gdk_window_get_display (window); n_monitors = gdk_display_get_n_monitors (display); monitor = gdk_display_get_monitor_at_window (display, window); if (minimized && maximized) minimized = FALSE; halfsnapped = (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT || impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT || impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP); switch (combo) { case GDK_WIN32_AEROSNAP_COMBO_NOTHING: /* Do nothing */ break; case GDK_WIN32_AEROSNAP_COMBO_UP: if (!maximized) { unsnap (window, monitor); gdk_window_maximize (window); } break; case GDK_WIN32_AEROSNAP_COMBO_DOWN: case GDK_WIN32_AEROSNAP_COMBO_SHIFTDOWN: if (maximized) { gdk_window_unmaximize (window); unsnap (window, monitor); } else if (halfsnapped) unsnap (window, monitor); else if (!minimized) gdk_window_iconify (window); break; case GDK_WIN32_AEROSNAP_COMBO_LEFT: if (maximized) gdk_window_unmaximize (window); if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED || impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP) { unsnap (window, monitor); snap_left (window, monitor, monitor); } else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT) { unsnap (window, monitor); snap_right (window, monitor, gdk_monitor_is_primary (monitor) ? monitor : gdk_display_get_monitor (display, n_monitors - 1)); } else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT) { unsnap (window, monitor); } break; case GDK_WIN32_AEROSNAP_COMBO_RIGHT: if (maximized) gdk_window_unmaximize (window); if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED || impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP) { unsnap (window, monitor); snap_right (window, monitor, monitor); } else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT) { unsnap (window, monitor); } else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT) { gint i; unsnap (window, monitor); if (n_monitors == 1 || monitor == gdk_display_get_monitor (display, n_monitors - 1)) { snap_left (window, monitor, monitor); } else { for (i = 0; i < n_monitors; i++) { if (monitor == gdk_display_get_monitor (display, i)) break; } snap_left (window, monitor, gdk_display_get_monitor (display, i + 1)); } } break; case GDK_WIN32_AEROSNAP_COMBO_SHIFTUP: if (!maximized && impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED) { snap_up (window); } break; case GDK_WIN32_AEROSNAP_COMBO_SHIFTLEFT: case GDK_WIN32_AEROSNAP_COMBO_SHIFTRIGHT: /* No implementation needed at the moment */ break; } } static void apply_snap (GdkWindow *window, GdkWin32AeroSnapState snap) { GdkMonitor *monitor; GdkDisplay *display; display = gdk_window_get_display (window); monitor = gdk_display_get_monitor_at_window (display, window); switch (snap) { case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED: break; case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE: unsnap (window, monitor); gdk_window_maximize (window); break; case GDK_WIN32_AEROSNAP_STATE_HALFLEFT: unsnap (window, monitor); snap_left (window, monitor, monitor); break; case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT: unsnap (window, monitor); snap_right (window, monitor, monitor); break; case GDK_WIN32_AEROSNAP_STATE_FULLUP: snap_up (window); break; } } /* Registers a dumb window class. This window * has DefWindowProc() for a window procedure and * does not do anything that GdkWindow-bound HWNDs do. */ static ATOM RegisterGdkDumbClass () { static ATOM klassDUMB = 0; static WNDCLASSEXW wcl; ATOM klass = 0; wcl.cbSize = sizeof (WNDCLASSEX); wcl.style = 0; /* DON'T set CS_REDRAW. It causes total redraw * on WM_SIZE and WM_MOVE. Flicker, Performance! */ wcl.lpfnWndProc = DefWindowProcW; wcl.cbClsExtra = 0; wcl.cbWndExtra = 0; wcl.hInstance = _gdk_app_hmodule; wcl.hIcon = 0; wcl.hIconSm = 0; wcl.lpszMenuName = NULL; wcl.hbrBackground = NULL; wcl.hCursor = LoadCursor (NULL, IDC_ARROW); wcl.style |= CS_OWNDC; wcl.lpszClassName = L"gdkWindowDumb"; if (klassDUMB == 0) klassDUMB = RegisterClassExW (&wcl); klass = klassDUMB; if (klass == 0) { WIN32_API_FAILED ("RegisterClassExW"); g_error ("That is a fatal error"); } return klass; } static gboolean ensure_snap_indicator_exists (GdkW32DragMoveResizeContext *context) { if (context->shape_indicator == NULL) { HWND handle; ATOM klass; klass = RegisterGdkDumbClass (); handle = CreateWindowExW (WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE, MAKEINTRESOURCEW (klass), L"", WS_POPUP, 0, 0, 0, 0, NULL, NULL, _gdk_app_hmodule, NULL); context->shape_indicator = handle; } return context->shape_indicator != NULL; } static gboolean ensure_snap_indicator_surface (GdkW32DragMoveResizeContext *context, gint width, gint height, guint scale) { if (context->indicator_surface != NULL && (context->indicator_surface_width < width || context->indicator_surface_height < height)) { cairo_surface_destroy (context->indicator_surface); context->indicator_surface = NULL; } if (context->indicator_surface == NULL) context->indicator_surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width * scale, height * scale); if (cairo_surface_status (context->indicator_surface) != CAIRO_STATUS_SUCCESS) { cairo_surface_destroy (context->indicator_surface); context->indicator_surface = NULL; return FALSE; } return TRUE; } /* Indicator is drawn with some inward offset, so that it does * not hug screen edges. */ static void adjust_indicator_rectangle (GdkRectangle *rect, gboolean inward) { gdouble inverter; const gint gap = AEROSNAP_INDICATOR_EDGE_GAP; #if defined(MORE_AEROSNAP_DEBUGGING) GdkRectangle cache = *rect; #endif if (inward) inverter = 1.0; else inverter = -1.0; rect->x += (gap * inverter); rect->y += (gap * inverter); rect->width -= (gap * 2 * inverter); rect->height -= (gap * 2 * inverter); #if defined(MORE_AEROSNAP_DEBUGGING) GDK_NOTE (MISC, g_print ("Adjusted %d x %d @ %d : %d -> %d x %d @ %d : %d\n", cache.width, cache.height, cache.x, cache.y, rect->width, rect->height, rect->x, rect->y)); #endif } static void rounded_rectangle (cairo_t *cr, gint x, gint y, gint width, gint height, gdouble radius, gdouble line_width, GdkRGBA *fill, GdkRGBA *outline) { gdouble degrees = M_PI / 180.0; if (fill == NULL && outline == NULL) return; cairo_save (cr); cairo_new_sub_path (cr); cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees); cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees); cairo_arc (cr, (x + radius), y + height - radius, radius, 90 * degrees, 180 * degrees); cairo_arc (cr, (x + radius), (y + radius), radius, 180 * degrees, 270 * degrees); cairo_close_path (cr); if (fill) { cairo_set_source_rgba (cr, fill->red, fill->green, fill->blue, fill->alpha); if (outline) cairo_fill_preserve (cr); else cairo_fill (cr); } if (outline) { cairo_set_source_rgba (cr, outline->red, outline->green, outline->blue, outline->alpha); cairo_set_line_width (cr, line_width); cairo_stroke (cr); } cairo_restore (cr); } /* Translates linear animation scale into some kind of curve */ static gdouble curve (gdouble val) { /* TODO: try different curves. For now it's just linear */ return val; } static gboolean draw_indicator (GdkW32DragMoveResizeContext *context, gint64 timestamp) { cairo_t *cr; GdkRGBA outline = {0, 0, 1.0, 1.0}; GdkRGBA fill = {0, 0, 1.0, 0.8}; GdkRectangle current_rect; gint64 current_time = g_get_monotonic_time (); gdouble animation_progress; gboolean last_draw; gdouble line_width; gdouble corner_radius; gint64 animation_duration; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (context->window->impl); line_width = AEROSNAP_INDICATOR_LINE_WIDTH * impl->window_scale; corner_radius = AEROSNAP_INDICATOR_CORNER_RADIUS; animation_duration = AEROSNAP_INDICATOR_ANIMATION_DURATION; last_draw = FALSE; if (timestamp == 0 && current_time - context->indicator_start_time > animation_duration) { timestamp = context->indicator_start_time + animation_duration; last_draw = TRUE; } if (timestamp != 0) current_time = timestamp; animation_progress = (gdouble) (current_time - context->indicator_start_time) / animation_duration; if (animation_progress > 1.0) animation_progress = 1.0; if (animation_progress < 0) animation_progress = 0; animation_progress = curve (animation_progress); current_rect = context->indicator_start; current_rect.x += (context->indicator_target.x - context->indicator_start.x) * animation_progress; current_rect.y += (context->indicator_target.y - context->indicator_start.y) * animation_progress; current_rect.width += (context->indicator_target.width - context->indicator_start.width) * animation_progress; current_rect.height += (context->indicator_target.height - context->indicator_start.height) * animation_progress; if (context->op == GDK_WIN32_DRAGOP_RESIZE && last_draw) { switch (context->edge) { case GDK_WINDOW_EDGE_NORTH_WEST: current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width); current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height); break; case GDK_WINDOW_EDGE_NORTH: current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height); break; case GDK_WINDOW_EDGE_WEST: current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width); break; case GDK_WINDOW_EDGE_SOUTH_WEST: current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width); current_rect.y = context->indicator_target.y; break; case GDK_WINDOW_EDGE_NORTH_EAST: current_rect.x = context->indicator_target.x; current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height); break; case GDK_WINDOW_EDGE_SOUTH_EAST: current_rect.x = context->indicator_target.x; current_rect.y = context->indicator_target.y; break; case GDK_WINDOW_EDGE_SOUTH: current_rect.y = context->indicator_target.y; break; case GDK_WINDOW_EDGE_EAST: current_rect.x = context->indicator_target.x; break; } } cr = cairo_create (context->indicator_surface); rounded_rectangle (cr, (current_rect.x - context->indicator_window_rect.x) * impl->window_scale, (current_rect.y - context->indicator_window_rect.y) * impl->window_scale, current_rect.width * impl->window_scale, current_rect.height * impl->window_scale, corner_radius, line_width, &fill, &outline); cairo_destroy (cr); #if defined(MORE_AEROSNAP_DEBUGGING) GDK_NOTE (MISC, g_print ("Indicator is %d x %d @ %d : %d; current time is %" G_GINT64_FORMAT "\n", current_rect.width, current_rect.height, current_rect.x - context->indicator_window_rect.x, current_rect.y - context->indicator_window_rect.y, current_time)); #endif return last_draw; } static gboolean redraw_indicator (gpointer user_data) { GdkW32DragMoveResizeContext *context = user_data; POINT window_position; SIZE window_size; BLENDFUNCTION blender; HDC hdc; POINT source_point = { 0, 0 }; gboolean last_draw; gdouble indicator_opacity; GdkWindowImplWin32 *impl; gboolean do_source_remove = FALSE; indicator_opacity = AEROSNAP_INDICATOR_OPACITY; if (GDK_WINDOW_DESTROYED (context->window) || !ensure_snap_indicator_exists (context)) { do_source_remove = TRUE; } impl = GDK_WINDOW_IMPL_WIN32 (context->window->impl); if (!ensure_snap_indicator_surface (context, context->indicator_window_rect.width, context->indicator_window_rect.height, impl->window_scale)) { do_source_remove = TRUE; } if (do_source_remove) { context->timer = 0; return G_SOURCE_REMOVE; } last_draw = draw_indicator (context, context->draw_timestamp); window_position.x = (context->indicator_window_rect.x - _gdk_offset_x) * impl->window_scale; window_position.y = (context->indicator_window_rect.y - _gdk_offset_y) * impl->window_scale; window_size.cx = context->indicator_window_rect.width * impl->window_scale; window_size.cy = context->indicator_window_rect.height * impl->window_scale; blender.BlendOp = AC_SRC_OVER; blender.BlendFlags = 0; blender.AlphaFormat = AC_SRC_ALPHA; blender.SourceConstantAlpha = 255 * indicator_opacity; hdc = cairo_win32_surface_get_dc (context->indicator_surface); API_CALL (SetWindowPos, (context->shape_indicator, GDK_WINDOW_HWND (context->window), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW | SWP_SHOWWINDOW | SWP_NOACTIVATE)); #if defined(MORE_AEROSNAP_DEBUGGING) GDK_NOTE (MISC, g_print ("Indicator window position is %ld x %ld @ %ld : %ld\n", window_size.cx, window_size.cy, window_position.x, window_position.y)); #endif API_CALL (UpdateLayeredWindow, (context->shape_indicator, NULL, &window_position, &window_size, hdc, &source_point, 0, &blender, ULW_ALPHA)); if (last_draw) context->timer = 0; return last_draw ? G_SOURCE_REMOVE : G_SOURCE_CONTINUE; } static GdkRectangle unity_of_rects (GdkRectangle a, GdkRectangle b) { GdkRectangle u = b; if (a.x < u.x) { u.width += u.x - a.x; u.x = a.x; } if (a.y < u.y) { u.height += (u.y - a.y); u.y = a.y; } if (a.x + a.width > u.x + u.width) u.width += (a.x + a.width) - (u.x + u.width); if (a.y + a.height > u.y + u.height) u.height += (a.y + a.height) - (u.y + u.height); #if defined(MORE_AEROSNAP_DEBUGGING) GDK_NOTE (MISC, g_print ("Unified 2 rects into %d x %d @ %d : %d\n", u.width, u.height, u.x, u.y)); #endif return u; } static void start_indicator_drawing (GdkW32DragMoveResizeContext *context, GdkRectangle from, GdkRectangle to, guint scale) { GdkRectangle to_adjusted, from_adjusted, from_or_to; gint64 indicator_animation_tick = AEROSNAP_INDICATOR_ANIMATION_TICK; GDK_NOTE (MISC, g_print ("Start drawing snap indicator %d x %d @ %d : %d -> %d x %d @ %d : %d\n", from.width * scale, from.height * scale, from.x, from.y, to.width * scale, to.height * scale, to.x, to.y)); if (GDK_WINDOW_DESTROYED (context->window)) return; if (!ensure_snap_indicator_exists (context)) return; from_or_to = unity_of_rects (from, to); if (!ensure_snap_indicator_surface (context, from_or_to.width, from_or_to.height, scale)) return; to_adjusted = to; adjust_indicator_rectangle (&to_adjusted, TRUE); from_adjusted = from; adjust_indicator_rectangle (&from_adjusted, TRUE); context->draw_timestamp = 0; context->indicator_start = from_adjusted; context->indicator_target = to_adjusted; context->indicator_window_rect = from_or_to; context->indicator_start_time = g_get_monotonic_time (); if (context->timer) { g_source_remove (context->timer); context->timer = 0; } context->timer = g_timeout_add_full (G_PRIORITY_DEFAULT, indicator_animation_tick, redraw_indicator, context, NULL); } static void update_fullup_indicator (GdkWindow *window, GdkW32DragMoveResizeContext *context) { SHORT maxysize; GdkRectangle from, to; GdkRectangle to_adjusted, from_adjusted, from_or_to; GdkWindowImplWin32 *impl; GDK_NOTE (MISC, g_print ("Update fullup indicator\n")); if (GDK_WINDOW_DESTROYED (context->window)) return; if (context->shape_indicator == NULL) return; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN); gdk_window_get_position (window, &to.x, &to.y); to.width = gdk_window_get_width (window); to.height = gdk_window_get_height (window); to.y = 0; to.height = maxysize; from = context->indicator_target; if (context->timer == 0) { from_adjusted = from; adjust_indicator_rectangle (&from_adjusted, FALSE); GDK_NOTE (MISC, g_print ("Restart fullup animation from %d x %d @ %d : %d -> %d x %d @ %d x %d\n", context->indicator_target.width, context->indicator_target.height, context->indicator_target.x, context->indicator_target.y, to.width, to.height, to.x, to.y)); start_indicator_drawing (context, from_adjusted, to, impl->window_scale); return; } from_or_to = unity_of_rects (from, to); to_adjusted = to; adjust_indicator_rectangle (&to_adjusted, TRUE); GDK_NOTE (MISC, g_print ("Retarget fullup animation %d x %d @ %d : %d -> %d x %d @ %d x %d\n", context->indicator_target.width, context->indicator_target.height, context->indicator_target.x, context->indicator_target.y, to_adjusted.width, to_adjusted.height, to_adjusted.x, to_adjusted.y)); context->indicator_target = to_adjusted; context->indicator_window_rect = from_or_to; ensure_snap_indicator_surface (context, from_or_to.width, from_or_to.height, impl->window_scale); } static void start_indicator (GdkWindow *window, GdkW32DragMoveResizeContext *context, gint x, gint y, GdkWin32AeroSnapState state) { GdkMonitor *monitor; GdkRectangle workarea; SHORT maxysize; GdkRectangle start_size, end_size; GdkDisplay *display; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); display = gdk_window_get_display (window); monitor = gdk_display_get_monitor_at_point (display, x, y); gdk_monitor_get_workarea (monitor, &workarea); maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN) / impl->window_scale; gdk_window_get_position (window, &start_size.x, &start_size.y); start_size.width = gdk_window_get_width (window); start_size.height = gdk_window_get_height (window); end_size = start_size; switch (state) { case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED: return; case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE: end_size.x = workarea.x; end_size.y = workarea.y; end_size.width = workarea.width; end_size.height = workarea.height; break; case GDK_WIN32_AEROSNAP_STATE_HALFLEFT: end_size.x = workarea.x; end_size.y = workarea.y; end_size.width = workarea.width / 2; end_size.height = workarea.height; break; case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT: end_size.x = (workarea.x + workarea.width / 2); end_size.y = workarea.y; end_size.width = workarea.width / 2; end_size.height = workarea.height; break; case GDK_WIN32_AEROSNAP_STATE_FULLUP: end_size.y = 0; end_size.height = maxysize; break; } start_indicator_drawing (context, start_size, end_size, impl->window_scale); } static void stop_indicator (GdkWindow *window, GdkW32DragMoveResizeContext *context) { GDK_NOTE (MISC, g_print ("Stop drawing snap indicator\n")); if (context->timer) { g_source_remove (context->timer); context->timer = 0; } API_CALL (SetWindowPos, (context->shape_indicator, SWP_NOZORDER_SPECIFIED, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW | SWP_HIDEWINDOW | SWP_NOACTIVATE)); } static gint point_in_aerosnap_region (gint x, gint y, AeroSnapEdgeRegion *region) { gint edge, trigger; edge = (x >= region->edge.x && y >= region->edge.y && x <= region->edge.x + region->edge.width && y <= region->edge.y + region->edge.height) ? 1 : 0; trigger = (x >= region->trigger.x && y >= region->trigger.y && x <= region->trigger.x + region->trigger.width && y <= region->trigger.y + region->trigger.height) ? 1 : 0; return edge + trigger; } static void handle_aerosnap_move_resize (GdkWindow *window, GdkW32DragMoveResizeContext *context, gint x, gint y) { gint i; AeroSnapEdgeRegion *reg; gint maximize = 0; gint halfleft = 0; gint halfright = 0; gint fullup = 0; gboolean fullup_edge = FALSE; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); if (context->op == GDK_WIN32_DRAGOP_RESIZE) switch (context->edge) { case GDK_WINDOW_EDGE_NORTH_WEST: case GDK_WINDOW_EDGE_NORTH_EAST: case GDK_WINDOW_EDGE_WEST: case GDK_WINDOW_EDGE_EAST: case GDK_WINDOW_EDGE_SOUTH_WEST: case GDK_WINDOW_EDGE_SOUTH_EAST: break; case GDK_WINDOW_EDGE_SOUTH: case GDK_WINDOW_EDGE_NORTH: fullup_edge = TRUE; break; } for (i = 0; i < context->maximize_regions->len && maximize == 0; i++) { reg = &g_array_index (context->maximize_regions, AeroSnapEdgeRegion, i); maximize = point_in_aerosnap_region (x, y, reg); } for (i = 0; i < context->halfleft_regions->len && halfleft == 0; i++) { reg = &g_array_index (context->halfleft_regions, AeroSnapEdgeRegion, i); halfleft = point_in_aerosnap_region (x, y, reg); } for (i = 0; i < context->halfright_regions->len && halfright == 0; i++) { reg = &g_array_index (context->halfright_regions, AeroSnapEdgeRegion, i); halfright = point_in_aerosnap_region (x, y, reg); } for (i = 0; i < context->fullup_regions->len && fullup == 0; i++) { reg = &g_array_index (context->fullup_regions, AeroSnapEdgeRegion, i); fullup = point_in_aerosnap_region (x, y, reg); } #if defined(MORE_AEROSNAP_DEBUGGING) GDK_NOTE (MISC, g_print ("AeroSnap: point %d : %d - max: %d, left %d, right %d, up %d\n", x, y, maximize, halfleft, halfright, fullup)); #endif if (!context->revealed) { if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize == 2) { context->revealed = TRUE; context->current_snap = GDK_WIN32_AEROSNAP_STATE_MAXIMIZE; start_indicator (window, context, x, y, context->current_snap); } else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft == 2) { context->revealed = TRUE; context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFLEFT; start_indicator (window, context, x, y, context->current_snap); } else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright == 2) { context->revealed = TRUE; context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT; start_indicator (window, context, x, y, context->current_snap); } else if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup == 2 && fullup_edge) { context->revealed = TRUE; context->current_snap = GDK_WIN32_AEROSNAP_STATE_FULLUP; start_indicator (window, context, x, y, context->current_snap); } return; } switch (context->current_snap) { case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED: if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup > 0) { context->current_snap = GDK_WIN32_AEROSNAP_STATE_FULLUP; start_indicator (window, context, x, y, context->current_snap); } break; case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE: if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize > 0) break; if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft > 0) { context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFLEFT; start_indicator (window, context, x, y, context->current_snap); } else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright > 0) { context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT; start_indicator (window, context, x, y, context->current_snap); } else { context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; stop_indicator (window, context); context->revealed = FALSE; } break; case GDK_WIN32_AEROSNAP_STATE_HALFLEFT: if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft > 0) break; if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize > 0) { context->current_snap = GDK_WIN32_AEROSNAP_STATE_MAXIMIZE; start_indicator (window, context, x, y, context->current_snap); } else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright > 0) { context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT; start_indicator (window, context, x, y, context->current_snap); } else { context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; stop_indicator (window, context); context->revealed = FALSE; } break; case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT: if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright > 0) break; if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize > 0) { context->current_snap = GDK_WIN32_AEROSNAP_STATE_MAXIMIZE; start_indicator (window, context, x, y, context->current_snap); } else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft > 0) { context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFLEFT; start_indicator (window, context, x, y, context->current_snap); } else { context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; stop_indicator (window, context); context->revealed = FALSE; } break; case GDK_WIN32_AEROSNAP_STATE_FULLUP: if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup > 0 && fullup_edge) { update_fullup_indicator (window, context); break; } context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; stop_indicator (window, context); break; } } static const gchar * get_cursor_name_from_op (GdkW32WindowDragOp op, GdkWindowEdge edge) { switch (op) { case GDK_WIN32_DRAGOP_MOVE: return "move"; case GDK_WIN32_DRAGOP_RESIZE: switch (edge) { case GDK_WINDOW_EDGE_NORTH_WEST: return "nw-resize"; case GDK_WINDOW_EDGE_NORTH: return "n-resize"; case GDK_WINDOW_EDGE_NORTH_EAST: return "ne-resize"; case GDK_WINDOW_EDGE_WEST: return "w-resize"; case GDK_WINDOW_EDGE_EAST: return "e-resize"; case GDK_WINDOW_EDGE_SOUTH_WEST: return "sw-resize"; case GDK_WINDOW_EDGE_SOUTH: return "s-resize"; case GDK_WINDOW_EDGE_SOUTH_EAST: return "se-resize"; } /* default: warn about unhandled enum values, * fallthrough to GDK_WIN32_DRAGOP_NONE case */ case GDK_WIN32_DRAGOP_COUNT: g_assert_not_reached (); case GDK_WIN32_DRAGOP_NONE: return "default"; /* default: warn about unhandled enum values */ } g_assert_not_reached (); return NULL; } static gboolean point_in_window (GdkWindow *window, gdouble x, gdouble y) { return x >= 0 && x < window->width && y >= 0 && y < window->height && (window->shape == NULL || cairo_region_contains_point (window->shape, x, y)) && (window->input_shape == NULL || cairo_region_contains_point (window->input_shape, x, y)); } static GdkWindow * child_window_at_coordinates (GdkWindow *window, gint root_x, gint root_y) { gint x, y; GList *l; GList *children; children = gdk_window_peek_children (window); gdk_window_get_root_origin (window, &x, &y); x = root_x - x; y = root_y - y; for (l = children; l; l = g_list_next (l)) { GdkWindow *child = GDK_WINDOW (l->data); if (point_in_window (child, x, y)) return child; } return window; } static void setup_drag_move_resize_context (GdkWindow *window, GdkW32DragMoveResizeContext *context, GdkW32WindowDragOp op, GdkWindowEdge edge, GdkDevice *device, gint button, gint root_x, gint root_y, guint32 timestamp) { RECT rect; const gchar *cursor_name; GdkWindow *pointer_window; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); GdkDisplay *display = gdk_device_get_display (device); gboolean maximized = gdk_window_get_state (window) & GDK_WINDOW_STATE_MAXIMIZED; /* Before we drag, we need to undo any maximization or snapping. * AeroSnap behaviour: * If snapped halfleft/halfright: * horizontal resize: * resize * don't unsnap * keep stashed unsnapped size intact * vertical resize: * resize * unsnap to new size (merge cached unsnapped state with current * snapped state in such a way that the gripped edge * does not move) * diagonal resize: * difficult to test (first move is usually either purely * horizontal or purely vertical, in which * case the above behaviour applies) * If snapped up: * horizontal resize: * resize * don't unsnap * apply new width and x position to unsnapped cache, * so that unsnapped window only regains its height * and y position, but inherits x and width from * the fullup snapped state * vertical resize: * unsnap to new size (merge cached unsnapped state with current * snapped state in such a way that the gripped edge * does not move) * * This implementation behaviour: * If snapped halfleft/halfright/fullup: * any resize: * unsnap to current size, discard cached pre-snap state * * TODO: make this implementation behave as AeroSnap on resizes? * There's also the case where * a halfleft/halfright window isn't unsnapped when it's * being moved horizontally, but it's more difficult to implement. */ if (op == GDK_WIN32_DRAGOP_RESIZE && (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT || impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT || impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP)) { discard_snapinfo (window); } else if (maximized || (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT || impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT || impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP)) { GdkMonitor *monitor; gint wx, wy, wwidth, wheight; gint swx, swy, swwidth, swheight; gboolean pointer_outside_of_window; gint offsetx, offsety; gboolean left_half; GdkDisplay *display; display = gdk_window_get_display (window); monitor = gdk_display_get_monitor_at_window (display, window); gdk_window_get_geometry (window, &wx, &wy, &wwidth, &wheight); swx = wx; swy = wy; swwidth = wwidth; swheight = wheight; /* Subtract window shadow. We don't want pointer to go outside of * the visible window during drag-move. For drag-resize it's OK. * Don't take shadow into account if the window is maximized - * maximized windows don't have shadows. */ if (op == GDK_WIN32_DRAGOP_MOVE && !maximized) { swx += impl->margins.left / impl->window_scale; swy += impl->margins.top / impl->window_scale; swwidth -= impl->margins_x; swheight -= impl->margins_y; } pointer_outside_of_window = root_x < swx || root_x > swx + swwidth || root_y < swy || root_y > swy + swheight; /* Calculate the offset of the pointer relative to the window */ offsetx = root_x - swx; offsety = root_y - swy; /* Figure out in which half of the window the pointer is. * The code currently only concerns itself with horizontal * dimension (left/right halves). * There's no upper/lower half, because usually window * is dragged by its upper half anyway. If that changes, adjust * accordingly. */ left_half = (offsetx < swwidth / 2); /* Inverse the offset for it to be from the right edge */ if (!left_half) offsetx = swwidth - offsetx; GDK_NOTE (MISC, g_print ("Pointer at %d : %d, this is %d : %d relative to the window's %s\n", root_x, root_y, offsetx, offsety, left_half ? "left half" : "right half")); /* Move window in such a way that on unmaximization/unsnapping the pointer * is still pointing at the appropriate half of the window, * with the same offset from the left or right edge. If the new * window size is too small, and adding that offset puts the pointer * into the other half or even beyond, move the pointer to the middle. */ if (!pointer_outside_of_window && maximized) { WINDOWPLACEMENT placement; gint unmax_width, unmax_height; gint shadow_unmax_width, shadow_unmax_height; placement.length = sizeof (placement); API_CALL (GetWindowPlacement, (GDK_WINDOW_HWND (window), &placement)); GDK_NOTE (MISC, g_print ("W32 WM unmaximized window placement is %ld x %ld @ %ld : %ld\n", placement.rcNormalPosition.right - placement.rcNormalPosition.left, placement.rcNormalPosition.bottom - placement.rcNormalPosition.top, placement.rcNormalPosition.left + _gdk_offset_x * impl->window_scale, placement.rcNormalPosition.top + _gdk_offset_y * impl->window_scale)); unmax_width = placement.rcNormalPosition.right - placement.rcNormalPosition.left; unmax_height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top; shadow_unmax_width = unmax_width - impl->margins_x * impl->window_scale; shadow_unmax_height = unmax_height - impl->margins_y * impl->window_scale; if (offsetx * impl->window_scale < (shadow_unmax_width / 2) && offsety * impl->window_scale < (shadow_unmax_height / 2)) { placement.rcNormalPosition.top = (root_y - offsety + impl->margins.top - _gdk_offset_y) * impl->window_scale; placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + unmax_height; if (left_half) { placement.rcNormalPosition.left = (root_x - offsetx + impl->margins.left - _gdk_offset_x) * impl->window_scale; placement.rcNormalPosition.right = placement.rcNormalPosition.left + unmax_width; } else { placement.rcNormalPosition.right = (root_x + offsetx + impl->margins.right - _gdk_offset_x) * impl->window_scale; placement.rcNormalPosition.left = placement.rcNormalPosition.right - unmax_width; } } else { placement.rcNormalPosition.left = (root_x * impl->window_scale) - (unmax_width / 2) - (_gdk_offset_x * impl->window_scale); if (offsety * impl->window_scale < shadow_unmax_height / 2) placement.rcNormalPosition.top = (root_y - offsety + impl->margins.top - _gdk_offset_y) * impl->window_scale; else placement.rcNormalPosition.top = (root_y * impl->window_scale) - (unmax_height / 2) - (_gdk_offset_y * impl->window_scale); placement.rcNormalPosition.right = placement.rcNormalPosition.left + unmax_width; placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + unmax_height; } GDK_NOTE (MISC, g_print ("Unmaximized window will be at %ld : %ld\n", placement.rcNormalPosition.left + _gdk_offset_x * impl->window_scale, placement.rcNormalPosition.top + _gdk_offset_y * impl->window_scale)); API_CALL (SetWindowPlacement, (GDK_WINDOW_HWND (window), &placement)); } else if (!pointer_outside_of_window && impl->snap_stash_int) { GdkRectangle new_pos; GdkRectangle snew_pos; new_pos.width = impl->snap_stash_int->width; new_pos.height = impl->snap_stash_int->height; snew_pos = new_pos; if (op == GDK_WIN32_DRAGOP_MOVE) { snew_pos.width -= impl->margins_x; snew_pos.height -= impl->margins_y; } if (offsetx < snew_pos.width / 2 && offsety < snew_pos.height / 2) { new_pos.y = root_y - offsety + impl->margins.top / impl->window_scale; if (left_half) new_pos.x = root_x - offsetx + impl->margins.left / impl->window_scale; else new_pos.x = root_x + offsetx + impl->margins.left / impl->window_scale - new_pos.width; } else { new_pos.x = root_x - new_pos.width / 2; new_pos.y = root_y - new_pos.height / 2; } GDK_NOTE (MISC, g_print ("Unsnapped window to %d : %d\n", new_pos.x, new_pos.y)); discard_snapinfo (window); gdk_window_move_resize (window, new_pos.x, new_pos.y, new_pos.width, new_pos.height); } if (maximized) gdk_window_unmaximize (window); else unsnap (window, monitor); if (pointer_outside_of_window) { /* Pointer outside of the window, move pointer into window */ GDK_NOTE (MISC, g_print ("Pointer at %d : %d is outside of %d x %d @ %d : %d, move it to %d : %d\n", root_x, root_y, wwidth, wheight, wx, wy, wx + wwidth / 2, wy + wheight / 2)); root_x = wx + wwidth / 2; /* This is Gnome behaviour. Windows WM would put the pointer * in the middle of the titlebar, but GDK doesn't know where * the titlebar is, if any. */ root_y = wy + wheight / 2; gdk_device_warp (device, root_x, root_y); } } _gdk_win32_get_window_rect (window, &rect); cursor_name = get_cursor_name_from_op (op, edge); context->cursor = gdk_cursor_new_from_name (cursor_name, NULL); pointer_window = child_window_at_coordinates (window, root_x, root_y); /* Note: This triggers a WM_CAPTURECHANGED, which will trigger * gdk_win32_window_end_move_resize_drag(), which will end * our op before it even begins, but only if context->op is not NONE. * This is why we first do the grab, *then* set the op. */ gdk_device_grab (device, pointer_window, GDK_OWNERSHIP_NONE, FALSE, GDK_ALL_EVENTS_MASK, context->cursor, timestamp); context->window = g_object_ref (window); context->op = op; context->edge = edge; context->device = device; context->button = button; context->start_root_x = root_x; context->start_root_y = root_y; context->timestamp = timestamp; context->start_rect = rect; context->shape_indicator = NULL; context->revealed = FALSE; context->halfleft_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion)); context->halfright_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion)); context->maximize_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion)); context->fullup_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion)); calculate_aerosnap_regions (context); GDK_NOTE (EVENTS, g_print ("begin drag moveresize: window %p, toplevel %p, " "op %u, edge %d, device %p, " "button %d, coord %d:%d, time %u\n", pointer_window, gdk_window_get_toplevel (window), context->op, context->edge, context->device, context->button, context->start_root_x, context->start_root_y, context->timestamp)); } void gdk_win32_window_end_move_resize_drag (GdkWindow *window) { GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); GdkW32DragMoveResizeContext *context = &impl->drag_move_resize_context; context->op = GDK_WIN32_DRAGOP_NONE; gdk_device_ungrab (context->device, GDK_CURRENT_TIME); g_clear_object (&context->cursor); context->revealed = FALSE; if (context->timer) { g_source_remove (context->timer); context->timer = 0; } g_clear_object (&context->window); if (context->indicator_surface) { cairo_surface_destroy (context->indicator_surface); context->indicator_surface = NULL; } if (context->shape_indicator) { stop_indicator (window, context); DestroyWindow (context->shape_indicator); context->shape_indicator = NULL; } g_clear_pointer (&context->halfleft_regions, g_array_unref); g_clear_pointer (&context->halfright_regions, g_array_unref); g_clear_pointer (&context->maximize_regions, g_array_unref); g_clear_pointer (&context->fullup_regions, g_array_unref); GDK_NOTE (EVENTS, g_print ("end drag moveresize: window %p, toplevel %p," "op %u, edge %d, device %p, " "button %d, coord %d:%d, time %u\n", window, gdk_window_get_toplevel (window), context->op, context->edge, context->device, context->button, context->start_root_x, context->start_root_y, context->timestamp)); if (context->current_snap != GDK_WIN32_AEROSNAP_STATE_UNDETERMINED) apply_snap (window, context->current_snap); context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; } static void gdk_win32_get_window_size_and_position_from_client_rect (GdkWindow *window, RECT *window_rect, SIZE *window_size, POINT *window_position) { GdkWindowImplWin32 *impl; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); /* Turn client area into window area */ _gdk_win32_adjust_client_rect (window, window_rect); /* Convert GDK screen coordinates to W32 desktop coordinates */ window_rect->left -= _gdk_offset_x * impl->window_scale; window_rect->right -= _gdk_offset_x * impl->window_scale; window_rect->top -= _gdk_offset_y * impl->window_scale; window_rect->bottom -= _gdk_offset_y * impl->window_scale; window_position->x = window_rect->left; window_position->y = window_rect->top; window_size->cx = window_rect->right - window_rect->left; window_size->cy = window_rect->bottom - window_rect->top; } static void gdk_win32_update_layered_window_from_cache (GdkWindow *window, RECT *client_rect) { POINT window_position; SIZE window_size; BLENDFUNCTION blender; HDC hdc; SIZE *window_size_ptr; POINT source_point = { 0, 0 }; POINT *source_point_ptr; GdkWindowImplWin32 *impl; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); gdk_win32_get_window_size_and_position_from_client_rect (window, client_rect, &window_size, &window_position); blender.BlendOp = AC_SRC_OVER; blender.BlendFlags = 0; blender.AlphaFormat = AC_SRC_ALPHA; blender.SourceConstantAlpha = impl->layered_opacity * 255; /* Size didn't change, so move immediately, no need to wait for redraw */ /* Strictly speaking, we don't need to supply hdc, source_point and * window_size here. However, without these arguments * the window moves but does not update its contents on Windows 7 when * desktop composition is off. This forces us to provide hdc and * source_point. window_size is here to avoid the function * inexplicably failing with error 317. */ if (gdk_display_is_composited (gdk_window_get_display (window))) { hdc = NULL; window_size_ptr = NULL; source_point_ptr = NULL; } else { hdc = cairo_win32_surface_get_dc (impl->cache_surface); window_size_ptr = &window_size; source_point_ptr = &source_point; } API_CALL (UpdateLayeredWindow, (GDK_WINDOW_HWND (window), NULL, &window_position, window_size_ptr, hdc, source_point_ptr, 0, &blender, ULW_ALPHA)); } void gdk_win32_window_do_move_resize_drag (GdkWindow *window, gint x, gint y) { RECT rect; RECT new_rect; gint diffy, diffx; MINMAXINFO mmi; GdkWindowImplWin32 *impl; GdkW32DragMoveResizeContext *context; gint width; gint height; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); context = &impl->drag_move_resize_context; if (!_gdk_win32_get_window_rect (window, &rect)) return; new_rect = context->start_rect; diffx = (x - context->start_root_x) * impl->window_scale; diffy = (y - context->start_root_y) * impl->window_scale; switch (context->op) { case GDK_WIN32_DRAGOP_RESIZE: switch (context->edge) { case GDK_WINDOW_EDGE_NORTH_WEST: new_rect.left += diffx; new_rect.top += diffy; break; case GDK_WINDOW_EDGE_NORTH: new_rect.top += diffy; break; case GDK_WINDOW_EDGE_NORTH_EAST: new_rect.right += diffx; new_rect.top += diffy; break; case GDK_WINDOW_EDGE_WEST: new_rect.left += diffx; break; case GDK_WINDOW_EDGE_EAST: new_rect.right += diffx; break; case GDK_WINDOW_EDGE_SOUTH_WEST: new_rect.left += diffx; new_rect.bottom += diffy; break; case GDK_WINDOW_EDGE_SOUTH: new_rect.bottom += diffy; break; case GDK_WINDOW_EDGE_SOUTH_EAST: default: new_rect.right += diffx; new_rect.bottom += diffy; break; } /* When handling WM_GETMINMAXINFO, mmi is already populated * by W32 WM and we apply our stuff on top of that. * Here it isn't, so we should at least clear it. */ memset (&mmi, 0, sizeof (mmi)); if (!_gdk_win32_window_fill_min_max_info (window, &mmi)) break; width = new_rect.right - new_rect.left; height = new_rect.bottom - new_rect.top; if (width > mmi.ptMaxTrackSize.x) { switch (context->edge) { case GDK_WINDOW_EDGE_NORTH_WEST: case GDK_WINDOW_EDGE_WEST: case GDK_WINDOW_EDGE_SOUTH_WEST: new_rect.left = new_rect.right - mmi.ptMaxTrackSize.x; break; case GDK_WINDOW_EDGE_NORTH_EAST: case GDK_WINDOW_EDGE_EAST: case GDK_WINDOW_EDGE_SOUTH_EAST: default: new_rect.right = new_rect.left + mmi.ptMaxTrackSize.x; break; } } else if (width < mmi.ptMinTrackSize.x) { switch (context->edge) { case GDK_WINDOW_EDGE_NORTH_WEST: case GDK_WINDOW_EDGE_WEST: case GDK_WINDOW_EDGE_SOUTH_WEST: new_rect.left = new_rect.right - mmi.ptMinTrackSize.x; break; case GDK_WINDOW_EDGE_NORTH_EAST: case GDK_WINDOW_EDGE_EAST: case GDK_WINDOW_EDGE_SOUTH_EAST: default: new_rect.right = new_rect.left + mmi.ptMinTrackSize.x; break; } } if (height > mmi.ptMaxTrackSize.y) { switch (context->edge) { case GDK_WINDOW_EDGE_NORTH_WEST: case GDK_WINDOW_EDGE_NORTH: case GDK_WINDOW_EDGE_NORTH_EAST: new_rect.top = new_rect.bottom - mmi.ptMaxTrackSize.y; case GDK_WINDOW_EDGE_SOUTH_WEST: case GDK_WINDOW_EDGE_SOUTH: case GDK_WINDOW_EDGE_SOUTH_EAST: default: new_rect.bottom = new_rect.top + mmi.ptMaxTrackSize.y; break; } } else if (height < mmi.ptMinTrackSize.y) { switch (context->edge) { case GDK_WINDOW_EDGE_NORTH_WEST: case GDK_WINDOW_EDGE_NORTH: case GDK_WINDOW_EDGE_NORTH_EAST: new_rect.top = new_rect.bottom - mmi.ptMinTrackSize.y; case GDK_WINDOW_EDGE_SOUTH_WEST: case GDK_WINDOW_EDGE_SOUTH: case GDK_WINDOW_EDGE_SOUTH_EAST: default: new_rect.bottom = new_rect.top + mmi.ptMinTrackSize.y; break; } } break; case GDK_WIN32_DRAGOP_MOVE: new_rect.left += diffx; new_rect.top += diffy; new_rect.right += diffx; new_rect.bottom += diffy; break; default: break; } if (context->op == GDK_WIN32_DRAGOP_RESIZE && (rect.left != new_rect.left || rect.right != new_rect.right || rect.top != new_rect.top || rect.bottom != new_rect.bottom)) { context->native_move_resize_pending = TRUE; _gdk_win32_do_emit_configure_event (window, new_rect); } else if (context->op == GDK_WIN32_DRAGOP_MOVE && (rect.left != new_rect.left || rect.top != new_rect.top)) { context->native_move_resize_pending = FALSE; _gdk_win32_do_emit_configure_event (window, new_rect); if (impl->layered) { gdk_win32_update_layered_window_from_cache (window, &new_rect); } else { SIZE window_size; POINT window_position; gdk_win32_get_window_size_and_position_from_client_rect (window, &new_rect, &window_size, &window_position); API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), SWP_NOZORDER_SPECIFIED, window_position.x, window_position.y, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE)); } } if (context->op == GDK_WIN32_DRAGOP_RESIZE || context->op == GDK_WIN32_DRAGOP_MOVE) handle_aerosnap_move_resize (window, context, x, y); } static void gdk_win32_window_begin_resize_drag (GdkWindow *window, GdkWindowEdge edge, GdkDevice *device, gint button, gint root_x, gint root_y, guint32 timestamp) { GdkWindowImplWin32 *impl; g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window) || IsIconic (GDK_WINDOW_HWND (window))) return; /* Tell Windows to start interactively resizing the window by pretending that * the left pointer button was clicked in the suitable edge or corner. This * will only work if the button is down when this function is called, and * will only work with button 1 (left), since Windows only allows window * dragging using the left mouse button. */ if (button != 1) return; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE) gdk_win32_window_end_move_resize_drag (window); setup_drag_move_resize_context (window, &impl->drag_move_resize_context, GDK_WIN32_DRAGOP_RESIZE, edge, device, button, root_x, root_y, timestamp); } static void gdk_win32_window_begin_move_drag (GdkWindow *window, GdkDevice *device, gint button, gint root_x, gint root_y, guint32 timestamp) { GdkWindowImplWin32 *impl; g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window) || IsIconic (GDK_WINDOW_HWND (window))) return; /* Tell Windows to start interactively moving the window by pretending that * the left pointer button was clicked in the titlebar. This will only work * if the button is down when this function is called, and will only work * with button 1 (left), since Windows only allows window dragging using the * left mouse button. */ if (button != 1) return; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE) gdk_win32_window_end_move_resize_drag (window); setup_drag_move_resize_context (window, &impl->drag_move_resize_context, GDK_WIN32_DRAGOP_MOVE, GDK_WINDOW_EDGE_NORTH_WEST, device, button, root_x, root_y, timestamp); } /* * Setting window states */ static void gdk_win32_window_iconify (GdkWindow *window) { HWND old_active_window; g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_window_iconify: %p: %s\n", GDK_WINDOW_HWND (window), _gdk_win32_window_state_to_string (window->state))); if (GDK_WINDOW_IS_MAPPED (window)) { old_active_window = GetActiveWindow (); GtkShowWindow (window, SW_MINIMIZE); if (old_active_window != GDK_WINDOW_HWND (window)) SetActiveWindow (old_active_window); } else { gdk_synthesize_window_state (window, 0, GDK_WINDOW_STATE_ICONIFIED); } } static void gdk_win32_window_deiconify (GdkWindow *window) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_window_deiconify: %p: %s\n", GDK_WINDOW_HWND (window), _gdk_win32_window_state_to_string (window->state))); if (GDK_WINDOW_IS_MAPPED (window)) { show_window_internal (window, GDK_WINDOW_IS_MAPPED (window), TRUE); } else { gdk_synthesize_window_state (window, GDK_WINDOW_STATE_ICONIFIED, 0); } } static void gdk_win32_window_stick (GdkWindow *window) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; /* FIXME: Do something? */ } static void gdk_win32_window_unstick (GdkWindow *window) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; /* FIXME: Do something? */ } static void gdk_win32_window_maximize (GdkWindow *window) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_window_maximize: %p: %s\n", GDK_WINDOW_HWND (window), _gdk_win32_window_state_to_string (window->state))); if (GDK_WINDOW_IS_MAPPED (window)) GtkShowWindow (window, SW_MAXIMIZE); else gdk_synthesize_window_state (window, 0, GDK_WINDOW_STATE_MAXIMIZED); } static void gdk_win32_window_unmaximize (GdkWindow *window) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_window_unmaximize: %p: %s\n", GDK_WINDOW_HWND (window), _gdk_win32_window_state_to_string (window->state))); if (GDK_WINDOW_IS_MAPPED (window)) GtkShowWindow (window, SW_RESTORE); else gdk_synthesize_window_state (window, GDK_WINDOW_STATE_MAXIMIZED, 0); } static void gdk_win32_window_fullscreen (GdkWindow *window) { gint x, y, width, height; FullscreenInfo *fi; HMONITOR monitor; MONITORINFO mi; g_return_if_fail (GDK_IS_WINDOW (window)); fi = g_new (FullscreenInfo, 1); if (!GetWindowRect (GDK_WINDOW_HWND (window), &(fi->r))) g_free (fi); else { GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); monitor = MonitorFromWindow (GDK_WINDOW_HWND (window), MONITOR_DEFAULTTONEAREST); mi.cbSize = sizeof (mi); if (monitor && GetMonitorInfo (monitor, &mi)) { x = mi.rcMonitor.left; y = mi.rcMonitor.top; width = mi.rcMonitor.right - x; height = mi.rcMonitor.bottom - y; } else { x = y = 0; width = GetSystemMetrics (SM_CXSCREEN); height = GetSystemMetrics (SM_CYSCREEN); } /* remember for restoring */ fi->hint_flags = impl->hint_flags; impl->hint_flags &= ~GDK_HINT_MAX_SIZE; g_object_set_data (G_OBJECT (window), "fullscreen-info", fi); fi->style = GetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE); /* Send state change before configure event */ gdk_synthesize_window_state (window, 0, GDK_WINDOW_STATE_FULLSCREEN); SetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE, (fi->style & ~WS_OVERLAPPEDWINDOW) | WS_POPUP); API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), HWND_TOP, x, y, width, height, SWP_NOCOPYBITS | SWP_SHOWWINDOW)); } } static void gdk_win32_window_unfullscreen (GdkWindow *window) { FullscreenInfo *fi; g_return_if_fail (GDK_IS_WINDOW (window)); fi = g_object_get_data (G_OBJECT (window), "fullscreen-info"); if (fi) { GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); gdk_synthesize_window_state (window, GDK_WINDOW_STATE_FULLSCREEN, 0); impl->hint_flags = fi->hint_flags; SetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE, fi->style); API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), HWND_NOTOPMOST, fi->r.left, fi->r.top, fi->r.right - fi->r.left, fi->r.bottom - fi->r.top, SWP_NOCOPYBITS | SWP_SHOWWINDOW)); g_object_set_data (G_OBJECT (window), "fullscreen-info", NULL); g_free (fi); _gdk_win32_window_update_style_bits (window); } } static void gdk_win32_window_set_keep_above (GdkWindow *window, gboolean setting) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_window_set_keep_above: %p: %s\n", GDK_WINDOW_HWND (window), setting ? "YES" : "NO")); if (GDK_WINDOW_IS_MAPPED (window)) { API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), setting ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)); } gdk_synthesize_window_state (window, setting ? GDK_WINDOW_STATE_BELOW : GDK_WINDOW_STATE_ABOVE, setting ? GDK_WINDOW_STATE_ABOVE : 0); } static void gdk_win32_window_set_keep_below (GdkWindow *window, gboolean setting) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_window_set_keep_below: %p: %s\n", GDK_WINDOW_HWND (window), setting ? "YES" : "NO")); if (GDK_WINDOW_IS_MAPPED (window)) { API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), setting ? HWND_BOTTOM : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)); } gdk_synthesize_window_state (window, setting ? GDK_WINDOW_STATE_ABOVE : GDK_WINDOW_STATE_BELOW, setting ? GDK_WINDOW_STATE_BELOW : 0); } static void gdk_win32_window_focus (GdkWindow *window, guint32 timestamp) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_window_focus: %p: %s\n", GDK_WINDOW_HWND (window), _gdk_win32_window_state_to_string (window->state))); if (window->state & GDK_WINDOW_STATE_MAXIMIZED) GtkShowWindow (window, SW_SHOWMAXIMIZED); else if (window->state & GDK_WINDOW_STATE_ICONIFIED) GtkShowWindow (window, SW_RESTORE); else if (!IsWindowVisible (GDK_WINDOW_HWND (window))) GtkShowWindow (window, SW_SHOWNORMAL); else GtkShowWindow (window, SW_SHOW); SetFocus (GDK_WINDOW_HWND (window)); } static void gdk_win32_window_set_modal_hint (GdkWindow *window, gboolean modal) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_window_set_modal_hint: %p: %s\n", GDK_WINDOW_HWND (window), modal ? "YES" : "NO")); if (modal == window->modal_hint) return; window->modal_hint = modal; #if 0 /* Not sure about this one.. -- Cody */ if (GDK_WINDOW_IS_MAPPED (window)) API_CALL (SetWindowPos, (GDK_WINDOW_HWND (window), modal ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)); #else if (modal) { _gdk_push_modal_window (window); gdk_window_raise (window); } else { _gdk_remove_modal_window (window); } #endif } static void gdk_win32_window_set_skip_taskbar_hint (GdkWindow *window, gboolean skips_taskbar) { static GdkWindow *owner = NULL; //GdkWindowAttr wa; g_return_if_fail (GDK_IS_WINDOW (window)); GDK_NOTE (MISC, g_print ("gdk_window_set_skip_taskbar_hint: %p: %s, doing nothing\n", GDK_WINDOW_HWND (window), skips_taskbar ? "YES" : "NO")); // ### TODO: Need to figure out what to do here. return; if (skips_taskbar) { #if 0 if (owner == NULL) { wa.window_type = GDK_WINDOW_TEMP; wa.wclass = GDK_INPUT_OUTPUT; wa.width = wa.height = 1; wa.event_mask = 0; owner = gdk_window_new_internal (NULL, &wa, 0, TRUE); } #endif SetWindowLongPtr (GDK_WINDOW_HWND (window), GWLP_HWNDPARENT, (LONG_PTR) GDK_WINDOW_HWND (owner)); #if 0 /* Should we also turn off the minimize and maximize buttons? */ SetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE, GetWindowLong (GDK_WINDOW_HWND (window), GWL_STYLE) & ~(WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_SYSMENU)); SetWindowPos (GDK_WINDOW_HWND (window), SWP_NOZORDER_SPECIFIED, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); #endif } else { SetWindowLongPtr (GDK_WINDOW_HWND (window), GWLP_HWNDPARENT, 0); } } static void gdk_win32_window_set_skip_pager_hint (GdkWindow *window, gboolean skips_pager) { g_return_if_fail (GDK_IS_WINDOW (window)); GDK_NOTE (MISC, g_print ("gdk_window_set_skip_pager_hint: %p: %s, doing nothing\n", GDK_WINDOW_HWND (window), skips_pager ? "YES" : "NO")); } static void gdk_win32_window_set_type_hint (GdkWindow *window, GdkWindowTypeHint hint) { g_return_if_fail (GDK_IS_WINDOW (window)); if (GDK_WINDOW_DESTROYED (window)) return; GDK_NOTE (MISC, G_STMT_START{ static GEnumClass *class = NULL; if (!class) class = g_type_class_ref (GDK_TYPE_WINDOW_TYPE_HINT); g_print ("gdk_window_set_type_hint: %p: %s\n", GDK_WINDOW_HWND (window), g_enum_get_value (class, hint)->value_name); }G_STMT_END); ((GdkWindowImplWin32 *)window->impl)->type_hint = hint; _gdk_win32_window_update_style_bits (window); } static GdkWindowTypeHint gdk_win32_window_get_type_hint (GdkWindow *window) { g_return_val_if_fail (GDK_IS_WINDOW (window), GDK_WINDOW_TYPE_HINT_NORMAL); if (GDK_WINDOW_DESTROYED (window)) return GDK_WINDOW_TYPE_HINT_NORMAL; return GDK_WINDOW_IMPL_WIN32 (window->impl)->type_hint; } static HRGN cairo_region_to_hrgn (const cairo_region_t *region, gint x_origin, gint y_origin, guint scale) { HRGN hrgn; RGNDATA *rgndata; RECT *rect; cairo_rectangle_int_t r; const int nrects = cairo_region_num_rectangles (region); guint nbytes = sizeof (RGNDATAHEADER) + (sizeof (RECT) * nrects); int i; rgndata = g_malloc (nbytes); rgndata->rdh.dwSize = sizeof (RGNDATAHEADER); rgndata->rdh.iType = RDH_RECTANGLES; rgndata->rdh.nCount = rgndata->rdh.nRgnSize = 0; SetRect (&rgndata->rdh.rcBound, G_MAXLONG, G_MAXLONG, G_MINLONG, G_MINLONG); for (i = 0; i < nrects; i++) { rect = ((RECT *) rgndata->Buffer) + rgndata->rdh.nCount++; cairo_region_get_rectangle (region, i, &r); rect->left = (r.x + x_origin) * scale; rect->right = (rect->left + r.width) * scale; rect->top = (r.y + y_origin) * scale; rect->bottom = (rect->top + r.height) * scale; if (rect->left < rgndata->rdh.rcBound.left) rgndata->rdh.rcBound.left = rect->left; if (rect->right > rgndata->rdh.rcBound.right) rgndata->rdh.rcBound.right = rect->right; if (rect->top < rgndata->rdh.rcBound.top) rgndata->rdh.rcBound.top = rect->top; if (rect->bottom > rgndata->rdh.rcBound.bottom) rgndata->rdh.rcBound.bottom = rect->bottom; } if ((hrgn = ExtCreateRegion (NULL, nbytes, rgndata)) == NULL) WIN32_API_FAILED ("ExtCreateRegion"); g_free (rgndata); return (hrgn); } static void gdk_win32_window_shape_combine_region (GdkWindow *window, const cairo_region_t *shape_region, gint offset_x, gint offset_y) { GdkWindowImplWin32 *impl; if (GDK_WINDOW_DESTROYED (window)) return; if (!shape_region) { GDK_NOTE (MISC, g_print ("gdk_win32_window_shape_combine_region: %p: none\n", GDK_WINDOW_HWND (window))); SetWindowRgn (GDK_WINDOW_HWND (window), NULL, TRUE); } else { HRGN hrgn; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); hrgn = cairo_region_to_hrgn (shape_region, 0, 0, impl->window_scale); GDK_NOTE (MISC, g_print ("gdk_win32_window_shape_combine_region: %p: %p\n", GDK_WINDOW_HWND (window), hrgn)); do_shape_combine_region (window, hrgn, offset_x, offset_y); } } GdkWindow * gdk_win32_window_lookup_for_display (GdkDisplay *display, HWND anid) { g_return_val_if_fail (display == gdk_display_get_default (), NULL); return (GdkWindow*) gdk_win32_handle_table_lookup (anid); } static void gdk_win32_window_set_opacity (GdkWindow *window, gdouble opacity) { LONG exstyle; typedef BOOL (WINAPI *PFN_SetLayeredWindowAttributes) (HWND, COLORREF, BYTE, DWORD); PFN_SetLayeredWindowAttributes setLayeredWindowAttributes = NULL; GdkWindowImplWin32 *impl; g_return_if_fail (GDK_IS_WINDOW (window)); if (!WINDOW_IS_TOPLEVEL (window) || GDK_WINDOW_DESTROYED (window)) return; if (opacity < 0) opacity = 0; else if (opacity > 1) opacity = 1; impl = GDK_WINDOW_IMPL_WIN32 (window->impl); if (impl->layered) { if (impl->layered_opacity != opacity) { RECT window_rect; impl->layered_opacity = opacity; gdk_win32_get_window_client_area_rect (window, impl->window_scale, &window_rect); gdk_win32_update_layered_window_from_cache (window, &window_rect); } return; } exstyle = GetWindowLong (GDK_WINDOW_HWND (window), GWL_EXSTYLE); if (!(exstyle & WS_EX_LAYERED)) SetWindowLong (GDK_WINDOW_HWND (window), GWL_EXSTYLE, exstyle | WS_EX_LAYERED); setLayeredWindowAttributes = (PFN_SetLayeredWindowAttributes)GetProcAddress (GetModuleHandle ("user32.dll"), "SetLayeredWindowAttributes"); if (setLayeredWindowAttributes) { API_CALL (setLayeredWindowAttributes, (GDK_WINDOW_HWND (window), 0, opacity * 0xff, LWA_ALPHA)); } } gboolean gdk_win32_window_is_win32 (GdkWindow *window) { return GDK_WINDOW_IS_WIN32 (window); } static gboolean gdk_win32_window_show_window_menu (GdkWindow *window, GdkEvent *event) { double event_x, event_y; gint x, y; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); switch (event->type) { case GDK_BUTTON_PRESS: case GDK_BUTTON_RELEASE: case GDK_TOUCH_BEGIN: case GDK_TOUCH_END: break; default: return FALSE; } gdk_event_get_root_coords (event, &event_x, &event_y); x = event_x - _gdk_offset_x; y = event_y - _gdk_offset_y; SendMessage (GDK_WINDOW_HWND (window), WM_SYSMENU, 0, MAKELPARAM (x * impl->window_scale, y * impl->window_scale)); return TRUE; } /** * _gdk_win32_acquire_dc * @impl: a Win32 #GdkWindowImplWin32 implementation * * Gets a DC with the given drawable selected into it. * * Returns: The DC, on success. Otherwise * %NULL. If this function succeeded * _gdk_win32_impl_release_dc() must be called * release the DC when you are done using it. **/ static HDC _gdk_win32_impl_acquire_dc (GdkWindowImplWin32 *impl) { if (GDK_IS_WINDOW_IMPL_WIN32 (impl) && GDK_WINDOW_DESTROYED (impl->wrapper)) return NULL; /* We don't call this function for layered windows, but * in case we do... */ if (impl->layered) return NULL; if (!impl->hdc) { impl->hdc = GetDC (impl->handle); if (!impl->hdc) WIN32_GDI_FAILED ("GetDC"); } if (impl->hdc) { impl->hdc_count++; return impl->hdc; } else { return NULL; } } /** * _gdk_win32_impl_release_dc * @impl: a Win32 #GdkWindowImplWin32 implementation * * Releases the reference count for the DC * from _gdk_win32_impl_acquire_dc() **/ static void _gdk_win32_impl_release_dc (GdkWindowImplWin32 *impl) { if (impl->layered) return; g_return_if_fail (impl->hdc_count > 0); impl->hdc_count--; if (impl->hdc_count == 0) { if (impl->saved_dc_bitmap) { GDI_CALL (SelectObject, (impl->hdc, impl->saved_dc_bitmap)); impl->saved_dc_bitmap = NULL; } if (impl->hdc) { GDI_CALL (ReleaseDC, (impl->handle, impl->hdc)); impl->hdc = NULL; } } } HWND gdk_win32_window_get_impl_hwnd (GdkWindow *window) { if (GDK_WINDOW_IS_WIN32 (window)) return GDK_WINDOW_HWND (window); return NULL; } static void gdk_win32_cairo_surface_destroy (void *data) { GdkWindowImplWin32 *impl = data; _gdk_win32_impl_release_dc (impl); impl->cairo_surface = NULL; } static cairo_surface_t * gdk_win32_ref_cairo_surface_layered (GdkWindow *window, GdkWindowImplWin32 *impl) { gint width, height; RECT window_rect; gdk_win32_get_window_client_area_rect (window, impl->window_scale, &window_rect); /* Turn client area into window area */ _gdk_win32_adjust_client_rect (window, &window_rect); width = window_rect.right - window_rect.left; height = window_rect.bottom - window_rect.top; if (width > impl->dib_width || height > impl->dib_height) { cairo_surface_t *new_cache; cairo_t *cr; /* Create larger cache surface, copy old cache surface over it */ new_cache = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height); if (impl->cache_surface) { cr = cairo_create (new_cache); cairo_set_source_surface (cr, impl->cache_surface, 0, 0); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_paint (cr); cairo_destroy (cr); cairo_surface_flush (new_cache); cairo_surface_destroy (impl->cache_surface); } impl->cache_surface = new_cache; cairo_surface_set_device_scale (impl->cache_surface, impl->window_scale, impl->window_scale); if (impl->cairo_surface) cairo_surface_destroy (impl->cairo_surface); impl->cairo_surface = NULL; } /* This is separate, because cairo_surface gets killed * off frequently by outside code, whereas cache_surface * is only killed by us, above. */ if (!impl->cairo_surface) { impl->cairo_surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height); impl->dib_width = width; impl->dib_height = height; cairo_surface_set_device_scale (impl->cairo_surface, impl->window_scale, impl->window_scale); cairo_surface_set_user_data (impl->cairo_surface, &gdk_win32_cairo_key, impl, gdk_win32_cairo_surface_destroy); } else { cairo_surface_reference (impl->cairo_surface); } return impl->cairo_surface; } static cairo_surface_t * gdk_win32_ref_cairo_surface (GdkWindow *window) { GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); if (GDK_IS_WINDOW_IMPL_WIN32 (impl) && GDK_WINDOW_DESTROYED (impl->wrapper)) return NULL; if (impl->layered) return gdk_win32_ref_cairo_surface_layered (window, impl); if (!impl->cairo_surface) { HDC hdc = _gdk_win32_impl_acquire_dc (impl); if (!hdc) return NULL; impl->cairo_surface = cairo_win32_surface_create_with_format (hdc, CAIRO_FORMAT_ARGB32); cairo_surface_set_device_scale (impl->cairo_surface, impl->window_scale, impl->window_scale); cairo_surface_set_user_data (impl->cairo_surface, &gdk_win32_cairo_key, impl, gdk_win32_cairo_surface_destroy); } else cairo_surface_reference (impl->cairo_surface); return impl->cairo_surface; } BOOL WINAPI GtkShowWindow (GdkWindow *window, int cmd_show) { cairo_t *cr; cairo_surface_t *surface; RECT window_rect; HDC hdc; POINT window_position; SIZE window_size; POINT source_point; BLENDFUNCTION blender; HWND hwnd = GDK_WINDOW_HWND (window); GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); switch (cmd_show) { case SW_FORCEMINIMIZE: case SW_HIDE: case SW_MINIMIZE: break; case SW_MAXIMIZE: case SW_RESTORE: case SW_SHOW: case SW_SHOWDEFAULT: case SW_SHOWMINIMIZED: case SW_SHOWMINNOACTIVE: case SW_SHOWNA: case SW_SHOWNOACTIVATE: case SW_SHOWNORMAL: if (IsWindowVisible (hwnd)) break; if ((WS_EX_LAYERED & GetWindowLongPtr (hwnd, GWL_EXSTYLE)) != WS_EX_LAYERED) break; /* Window was hidden, will be shown. Erase it, GDK will repaint soon, * but not soon enough, so it's possible to see old content before * the next redraw, unless we erase the window first. */ GetWindowRect (hwnd, &window_rect); source_point.x = source_point.y = 0; window_position.x = window_rect.left; window_position.y = window_rect.top; window_size.cx = window_rect.right - window_rect.left; window_size.cy = window_rect.bottom - window_rect.top; blender.BlendOp = AC_SRC_OVER; blender.BlendFlags = 0; blender.AlphaFormat = AC_SRC_ALPHA; blender.SourceConstantAlpha = 255; /* Create a surface of appropriate size and clear it */ surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, window_size.cx, window_size.cy); cairo_surface_set_device_scale (surface, impl->window_scale, impl->window_scale); cr = cairo_create (surface); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); cairo_paint (cr); cairo_destroy (cr); cairo_surface_flush (surface); hdc = cairo_win32_surface_get_dc (surface); /* No API_CALL() wrapper, don't check for errors */ UpdateLayeredWindow (hwnd, NULL, &window_position, &window_size, hdc, &source_point, 0, &blender, ULW_ALPHA); cairo_surface_destroy (surface); break; } /* Ensure that maximized window size is corrected later on */ if (cmd_show == SW_MAXIMIZE) impl->maximizing = TRUE; return ShowWindow (hwnd, cmd_show); } static void gdk_win32_window_set_shadow_width (GdkWindow *window, gint left, gint right, gint top, gint bottom) { GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); if (GDK_WINDOW_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_win32_window_set_shadow_width: window %p, " "left %d, top %d, right %d, bottom %d\n", window, left, top, right, bottom)); impl->zero_margins = left == 0 && right == 0 && top == 0 && bottom == 0; if (impl->zero_margins) return; impl->margins.left = left; impl->margins.right = right * impl->window_scale; impl->margins.top = top; impl->margins.bottom = bottom * impl->window_scale; impl->margins_x = left + right; impl->margins_y = top + bottom; } gint _gdk_win32_window_get_scale_factor (GdkWindow *window) { GdkDisplay *display; GdkWindowImplWin32 *impl; GdkWin32Display *win32_display; UINT dpix, dpiy; gboolean is_scale_acquired; if (GDK_WINDOW_DESTROYED (window)) return 1; g_return_val_if_fail (window != NULL, 1); display = gdk_window_get_display (window); impl = GDK_WINDOW_IMPL_WIN32 (window->impl); win32_display = GDK_WIN32_DISPLAY (display); if (win32_display->dpi_aware_type != PROCESS_DPI_UNAWARE) { if (win32_display->has_fixed_scale) impl->window_scale = win32_display->window_scale; else impl->window_scale = _gdk_win32_display_get_monitor_scale_factor (win32_display, NULL, GDK_WINDOW_HWND (window), NULL); return impl->window_scale; } else { if (win32_display->has_fixed_scale) { static gsize hidpi_msg_displayed = 0; if (g_once_init_enter (&hidpi_msg_displayed)) { g_message ("Note: GDK_SCALE is ignored as HiDPI awareness is disabled."); g_once_init_leave (&hidpi_msg_displayed, 1); } } /* Application is not DPI aware, don't bother */ return 1; } } void _gdk_win32_window_get_unscaled_size (GdkWindow *window, gint *unscaled_width, gint *unscaled_height) { GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); if (unscaled_width) *unscaled_width = impl->unscaled_width; if (unscaled_height) *unscaled_height = impl->unscaled_height; } static void gdk_win32_input_shape_combine_region (GdkWindow *window, const cairo_region_t *shape_region, gint offset_x, gint offset_y) { /* Partial input shape support is implemented by handling the * NC_NCHITTEST message */ } static void gdk_window_impl_win32_class_init (GdkWindowImplWin32Class *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GdkWindowImplClass *impl_class = GDK_WINDOW_IMPL_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->finalize = gdk_window_impl_win32_finalize; impl_class->ref_cairo_surface = gdk_win32_ref_cairo_surface; impl_class->show = gdk_win32_window_show; impl_class->hide = gdk_win32_window_hide; impl_class->withdraw = gdk_win32_window_withdraw; impl_class->set_events = gdk_win32_window_set_events; impl_class->get_events = gdk_win32_window_get_events; impl_class->raise = gdk_win32_window_raise; impl_class->lower = gdk_win32_window_lower; impl_class->restack_toplevel = gdk_win32_window_restack_toplevel; impl_class->move_resize = gdk_win32_window_move_resize; impl_class->get_geometry = gdk_win32_window_get_geometry; impl_class->get_device_state = gdk_window_win32_get_device_state; impl_class->get_root_coords = gdk_win32_window_get_root_coords; impl_class->shape_combine_region = gdk_win32_window_shape_combine_region; impl_class->input_shape_combine_region = gdk_win32_input_shape_combine_region; impl_class->destroy = gdk_win32_window_destroy; impl_class->begin_paint = gdk_win32_window_begin_paint; impl_class->end_paint = gdk_win32_window_end_paint; //impl_class->beep = gdk_x11_window_beep; impl_class->show_window_menu = gdk_win32_window_show_window_menu; impl_class->focus = gdk_win32_window_focus; impl_class->set_type_hint = gdk_win32_window_set_type_hint; impl_class->get_type_hint = gdk_win32_window_get_type_hint; impl_class->set_modal_hint = gdk_win32_window_set_modal_hint; impl_class->set_skip_taskbar_hint = gdk_win32_window_set_skip_taskbar_hint; impl_class->set_skip_pager_hint = gdk_win32_window_set_skip_pager_hint; impl_class->set_urgency_hint = gdk_win32_window_set_urgency_hint; impl_class->set_geometry_hints = gdk_win32_window_set_geometry_hints; impl_class->set_title = gdk_win32_window_set_title; impl_class->set_role = gdk_win32_window_set_role; //impl_class->set_startup_id = gdk_x11_window_set_startup_id; impl_class->set_transient_for = gdk_win32_window_set_transient_for; impl_class->get_frame_extents = gdk_win32_window_get_frame_extents; impl_class->set_accept_focus = gdk_win32_window_set_accept_focus; impl_class->set_focus_on_map = gdk_win32_window_set_focus_on_map; impl_class->set_icon_list = gdk_win32_window_set_icon_list; impl_class->set_icon_name = gdk_win32_window_set_icon_name; impl_class->iconify = gdk_win32_window_iconify; impl_class->deiconify = gdk_win32_window_deiconify; impl_class->stick = gdk_win32_window_stick; impl_class->unstick = gdk_win32_window_unstick; impl_class->maximize = gdk_win32_window_maximize; impl_class->unmaximize = gdk_win32_window_unmaximize; impl_class->fullscreen = gdk_win32_window_fullscreen; impl_class->unfullscreen = gdk_win32_window_unfullscreen; impl_class->set_keep_above = gdk_win32_window_set_keep_above; impl_class->set_keep_below = gdk_win32_window_set_keep_below; impl_class->get_group = gdk_win32_window_get_group; impl_class->set_group = gdk_win32_window_set_group; impl_class->set_decorations = gdk_win32_window_set_decorations; impl_class->get_decorations = gdk_win32_window_get_decorations; impl_class->set_functions = gdk_win32_window_set_functions; impl_class->set_shadow_width = gdk_win32_window_set_shadow_width; impl_class->begin_resize_drag = gdk_win32_window_begin_resize_drag; impl_class->begin_move_drag = gdk_win32_window_begin_move_drag; impl_class->set_opacity = gdk_win32_window_set_opacity; impl_class->destroy_notify = gdk_win32_window_destroy_notify; impl_class->register_dnd = _gdk_win32_window_register_dnd; impl_class->drag_begin = _gdk_win32_window_drag_begin; impl_class->create_gl_context = _gdk_win32_window_create_gl_context; impl_class->get_scale_factor = _gdk_win32_window_get_scale_factor; impl_class->get_unscaled_size = _gdk_win32_window_get_unscaled_size; } HGDIOBJ gdk_win32_window_get_handle (GdkWindow *window) { if (!GDK_WINDOW_IS_WIN32 (window)) { g_warning (G_STRLOC " window is not a native Win32 window"); return NULL; } return GDK_WINDOW_HWND (window); }