/* 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 "gdksurfaceimpl.h" #include "gdkprivate-win32.h" #include "gdkdeviceprivate.h" #include "gdkdevicemanager-win32.h" #include "gdkenumtypes.h" #include "gdkwin32.h" #include "gdkdisplayprivate.h" #include "gdkframeclockprivate.h" #include "gdkmonitorprivate.h" #include "gdkwin32surface.h" #include "gdkwin32cursor.h" #include "gdkglcontext-win32.h" #include "gdkdisplay-win32.h" #include #include #include #include "fallback-c89.c" static void gdk_surface_impl_win32_init (GdkSurfaceImplWin32 *window); static void gdk_surface_impl_win32_class_init (GdkSurfaceImplWin32Class *klass); static void gdk_surface_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; /* 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 void gdk_win32_impl_frame_clock_after_paint (GdkFrameClock *clock, GdkSurface *surface); static gboolean _gdk_surface_get_functions (GdkSurface *window, GdkWMFunction *functions); struct _GdkWin32Surface { GdkSurface parent; }; struct _GdkWin32SurfaceClass { GdkSurfaceClass parent_class; }; G_DEFINE_TYPE (GdkWin32Surface, gdk_win32_surface, GDK_TYPE_SURFACE) static void gdk_win32_surface_class_init (GdkWin32SurfaceClass *window_class) { } static void gdk_win32_surface_init (GdkWin32Surface *window) { } G_DEFINE_TYPE (GdkSurfaceImplWin32, gdk_surface_impl_win32, GDK_TYPE_SURFACE_IMPL) GType _gdk_surface_impl_win32_get_type (void) { static GType object_type = 0; if (!object_type) { const GTypeInfo object_info = { sizeof (GdkSurfaceImplWin32Class), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gdk_surface_impl_win32_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GdkSurfaceImplWin32), 0, /* n_preallocs */ (GInstanceInitFunc) gdk_surface_impl_win32_init, }; object_type = g_type_register_static (GDK_TYPE_SURFACE_IMPL, "GdkSurfaceImplWin32", &object_info, 0); } return object_type; } static void gdk_surface_impl_win32_init (GdkSurfaceImplWin32 *impl) { impl->hicon_big = NULL; impl->hicon_small = NULL; impl->hint_flags = 0; impl->type_hint = GDK_SURFACE_TYPE_HINT_NORMAL; impl->transient_owner = NULL; impl->transient_children = NULL; impl->num_transients = 0; impl->changing_state = FALSE; impl->surface_scale = 1; } static void gdk_surface_impl_win32_dispose (GObject *object) { GdkSurfaceImplWin32 *surface_impl; g_return_if_fail (GDK_IS_SURFACE_IMPL_WIN32 (object)); surface_impl = GDK_SURFACE_IMPL_WIN32 (object); g_clear_object (&surface_impl->cursor); G_OBJECT_CLASS (parent_class)->dispose (object); } static void gdk_surface_impl_win32_finalize (GObject *object) { GdkSurface *wrapper; GdkSurfaceImplWin32 *surface_impl; g_return_if_fail (GDK_IS_SURFACE_IMPL_WIN32 (object)); surface_impl = GDK_SURFACE_IMPL_WIN32 (object); wrapper = surface_impl->wrapper; if (!GDK_SURFACE_DESTROYED (wrapper)) { gdk_win32_handle_table_remove (surface_impl->handle); } g_clear_pointer (&surface_impl->snap_stash, g_free); g_clear_pointer (&surface_impl->snap_stash_int, g_free); if (surface_impl->hicon_big != NULL) { GDI_CALL (DestroyIcon, (surface_impl->hicon_big)); surface_impl->hicon_big = NULL; } if (surface_impl->hicon_small != NULL) { GDI_CALL (DestroyIcon, (surface_impl->hicon_small)); surface_impl->hicon_small = NULL; } g_free (surface_impl->decorations); if (surface_impl->cache_surface) { cairo_surface_destroy (surface_impl->cache_surface); surface_impl->cache_surface = NULL; } _gdk_win32_surface_unregister_dnd (wrapper); g_clear_object (&surface_impl->drop); g_assert (surface_impl->transient_owner == NULL); g_assert (surface_impl->transient_children == NULL); G_OBJECT_CLASS (parent_class)->finalize (object); } void _gdk_win32_get_window_client_area_rect (GdkSurface *window, gint scale, RECT *rect) { gint x, y, width, height; gdk_surface_get_position (window, &x, &y); width = gdk_surface_get_width (window); height = gdk_surface_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_impl_frame_clock_after_paint (GdkFrameClock *clock, GdkSurface *surface) { DWM_TIMING_INFO timing_info; LARGE_INTEGER tick_frequency; GdkFrameTimings *timings; timings = gdk_frame_clock_get_timings (clock, gdk_frame_clock_get_frame_counter (clock)); if (timings) { timings->refresh_interval = 16667; /* default to 1/60th of a second */ timings->presentation_time = 0; if (QueryPerformanceFrequency (&tick_frequency)) { HRESULT hr; timing_info.cbSize = sizeof (timing_info); hr = DwmGetCompositionTimingInfo (NULL, &timing_info); if (SUCCEEDED (hr)) { timings->refresh_interval = timing_info.qpcRefreshPeriod * (gdouble)G_USEC_PER_SEC / tick_frequency.QuadPart; timings->presentation_time = timing_info.qpcCompose * (gdouble)G_USEC_PER_SEC / tick_frequency.QuadPart; } } timings->complete = TRUE; } } void _gdk_win32_adjust_client_rect (GdkSurface *window, RECT *rect) { LONG style, exstyle; style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE); exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE); API_CALL (AdjustWindowRectEx, (rect, style, FALSE, exstyle)); } gboolean _gdk_win32_surface_enable_transparency (GdkSurface *window) { GdkSurfaceImplWin32 *impl; DWM_BLURBEHIND blur_behind; HRGN empty_region; HRESULT call_result; HWND parent, thiswindow; if (window == NULL || GDK_SURFACE_HWND (window) == NULL) return FALSE; impl = GDK_SURFACE_IMPL_WIN32 (window->impl); /* layered windows don't need blurbehind for transparency */ if (impl->layered) return TRUE; if (!gdk_display_is_composited (gdk_surface_get_display (window))) return FALSE; thiswindow = GDK_SURFACE_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 * GdkSurfaceType. 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 (GdkSurfaceType wtype, GdkSurfaceTypeHint wtype_hint) { static ATOM klassTOPLEVEL = 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_surface_procedure; wcl.cbClsExtra = 0; wcl.cbWndExtra = 0; wcl.hInstance = _gdk_dll_hinstance; wcl.hIcon = 0; wcl.hIconSm = 0; /* initialize once! */ if (0 == hAppIcon && 0 == hAppIconSm) { gchar sLoc [MAX_PATH+1]; if (0 != GetModuleFileName (_gdk_dll_hinstance, 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_SURFACE_TOPLEVEL: /* MSDN: CS_OWNDC is needed for OpenGL contexts */ wcl.style |= CS_OWNDC; if (0 == klassTOPLEVEL) { wcl.lpszClassName = L"gdkSurfaceToplevel"; ONCE_PER_CLASS (); klassTOPLEVEL = RegisterClassExW (&wcl); } klass = klassTOPLEVEL; break; case GDK_SURFACE_TEMP: if ((wtype_hint == GDK_SURFACE_TYPE_HINT_MENU) || (wtype_hint == GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU) || (wtype_hint == GDK_SURFACE_TYPE_HINT_POPUP_MENU)) { if (klassTEMPSHADOW == 0) { wcl.lpszClassName = L"gdkSurfaceTempShadow"; wcl.style |= CS_SAVEBITS; wcl.style |= 0x00020000; /* CS_DROPSHADOW */ ONCE_PER_CLASS (); klassTEMPSHADOW = RegisterClassExW (&wcl); } klass = klassTEMPSHADOW; } else { if (klassTEMP == 0) { wcl.lpszClassName = L"gdkSurfaceTemp"; wcl.style |= CS_SAVEBITS; ONCE_PER_CLASS (); klassTEMP = RegisterClassExW (&wcl); } klass = klassTEMP; } break; case GDK_SURFACE_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_surface_impl (GdkDisplay *display, GdkSurface *window, GdkSurface *real_parent, GdkSurfaceAttr *attributes) { HWND hwndNew; HANDLE hparent; ATOM klass = 0; DWORD dwStyle = 0, dwExStyle; RECT rect; GdkSurfaceImplWin32 *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; GdkFrameClock *frame_clock; g_return_if_fail (display == _gdk_display); GDK_NOTE (MISC, g_print ("_gdk_surface_impl_new: %s %s\n", (window->surface_type == GDK_SURFACE_TOPLEVEL ? "TOPLEVEL" : (window->surface_type == GDK_SURFACE_TEMP ? "TEMP" : "???")), (attributes->wclass == GDK_INPUT_OUTPUT ? "" : "input-only"))); hparent = (real_parent != NULL) ? GDK_SURFACE_HWND (real_parent) : NULL; impl = g_object_new (GDK_TYPE_SURFACE_IMPL_WIN32, NULL); impl->wrapper = GDK_SURFACE (window); window->impl = GDK_SURFACE_IMPL (impl); impl->layered = FALSE; impl->layered_opacity = 1.0; display_win32 = GDK_WIN32_DISPLAY (display); impl->surface_scale = _gdk_win32_display_get_monitor_scale_factor (display_win32, NULL, NULL, NULL); impl->unscaled_width = window->width * impl->surface_scale; impl->unscaled_height = window->height * impl->surface_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->surface_type) { case GDK_SURFACE_TOPLEVEL: if (window->parent) { /* The common code warns for this case. */ hparent = GetDesktopWindow (); } /* MSDN: We need WS_CLIPCHILDREN and WS_CLIPSIBLINGS for GL Context Creation */ if (window->surface_type == GDK_SURFACE_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_SURFACE_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_SURFACE_CHILD: default: g_assert_not_reached (); } rect.left = window->x * impl->surface_scale; rect.top = window->y * impl->surface_scale; rect.right = rect.left + window->width * impl->surface_scale; rect.bottom = rect.top + window->height * impl->surface_scale; AdjustWindowRectEx (&rect, dwStyle, FALSE, dwExStyle); real_x = (window->x - offset_x) * impl->surface_scale; real_y = (window->y - offset_y) * impl->surface_scale; if (window->surface_type == GDK_SURFACE_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: 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 = ""; if (impl->type_hint == GDK_SURFACE_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_SURFACE_TYPE_HINT_DND) dwExStyle |= WS_EX_TRANSPARENT; klass = RegisterGdkClass (window->surface_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_dll_hinstance, window); if (GDK_SURFACE_HWND (window) != hwndNew) { g_warning ("gdk_surface_new: gdk_event_translate::WM_CREATE (%p, %p) HWND mismatch.", GDK_SURFACE_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_SURFACE_HWND (window)); GDK_SURFACE_HWND (window) = hwndNew; gdk_win32_handle_table_insert (&GDK_SURFACE_HWND (window), window); # else /* the old behaviour, but with warning */ impl->handle = hwndNew; # endif } GetWindowRect (GDK_SURFACE_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_SURFACE_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_SURFACE_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_SURFACE_HWND (window))); /* Add window handle to title */ GDK_NOTE (MISC_OR_EVENTS, gdk_surface_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->surface_type == GDK_SURFACE_TEMP) // gdk_surface_set_skip_taskbar_hint (window, TRUE); _gdk_win32_surface_enable_transparency (window); frame_clock = gdk_surface_get_frame_clock (window); g_signal_connect (frame_clock, "after-paint", G_CALLBACK (gdk_win32_impl_frame_clock_after_paint), window); } static void gdk_win32_surface_destroy (GdkSurface *window, gboolean recursing, gboolean foreign_destroy) { GdkSurfaceImplWin32 *surface_impl = GDK_SURFACE_IMPL_WIN32 (window->impl); GSList *tmp; g_return_if_fail (GDK_IS_SURFACE (window)); GDK_NOTE (MISC, g_print ("gdk_win32_surface_destroy: %p\n", GDK_SURFACE_HWND (window))); /* Remove ourself from the modal stack */ _gdk_remove_modal_window (window); /* Remove all our transient children */ tmp = surface_impl->transient_children; while (tmp != NULL) { GdkSurface *child = tmp->data; GdkSurfaceImplWin32 *child_impl = GDK_SURFACE_IMPL_WIN32 (GDK_SURFACE (child)->impl); child_impl->transient_owner = NULL; tmp = tmp->next; } g_slist_free (surface_impl->transient_children); surface_impl->transient_children = NULL; /* Remove ourself from our transient owner */ if (surface_impl->transient_owner != NULL) { gdk_surface_set_transient_for (window, NULL); } if (!recursing && !foreign_destroy) { window->destroyed = TRUE; DestroyWindow (GDK_SURFACE_HWND (window)); } } /* This function is called when the window really gone. */ static void gdk_win32_surface_destroy_notify (GdkSurface *window) { g_return_if_fail (GDK_IS_SURFACE (window)); GDK_NOTE (EVENTS, g_print ("gdk_surface_destroy_notify: %p%s\n", GDK_SURFACE_HWND (window), (GDK_SURFACE_DESTROYED (window) ? " (destroyed)" : ""))); if (!GDK_SURFACE_DESTROYED (window)) { g_warning ("window %p unexpectedly destroyed", GDK_SURFACE_HWND (window)); _gdk_surface_destroy (window, TRUE); } gdk_win32_handle_table_remove (GDK_SURFACE_HWND (window)); g_object_unref (window); } static void get_outer_rect (GdkSurface *window, gint width, gint height, RECT *rect) { GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); rect->left = rect->top = 0; rect->right = width * impl->surface_scale; rect->bottom = height * impl->surface_scale; _gdk_win32_adjust_client_rect (window, rect); } static void adjust_for_gravity_hints (GdkSurface *window, RECT *outer_rect, gint *x, gint *y) { GdkSurfaceImplWin32 *impl = GDK_SURFACE_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->surface_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->surface_scale; *x += window->width; break; case GDK_GRAVITY_STATIC: *x += outer_rect->left / impl->surface_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->surface_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->surface_scale; *y += window->height; break; case GDK_GRAVITY_STATIC: *y += outer_rect->top * impl->surface_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 (GdkSurface *window, gboolean already_mapped, gboolean deiconify) { GdkSurfaceImplWin32 *surface_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_SURFACE_HWND (window), _gdk_win32_surface_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_SURFACE_STATE_ICONIFIED)) { GtkShowWindow (window, SW_SHOWMINNOACTIVE); return; } /* If asked to just show an iconified window, do nothing. */ if (!deiconify && (window->state & GDK_SURFACE_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_SURFACE_STATE_ICONIFIED)) return; /* If asked to show (but not raise) a window that is already * visible, do nothing. */ if (!deiconify && !already_mapped && IsWindowVisible (GDK_SURFACE_HWND (window))) return; /* Other cases */ if (!already_mapped) focus_on_map = window->focus_on_map; exstyle = GetWindowLong (GDK_SURFACE_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_SURFACE_TYPE (window) == GDK_SURFACE_TEMP || !focus_on_map) flags |= SWP_NOACTIVATE; SetWindowPos (GDK_SURFACE_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. */ surface_impl = GDK_SURFACE_IMPL_WIN32 (window->impl); if (!already_mapped && GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL && (surface_impl->hint_flags & (GDK_HINT_POS | GDK_HINT_USER_POS)) == 0) { gboolean center = FALSE; RECT window_rect, center_on_rect; int x, y; x = surface_impl->initial_x; y = surface_impl->initial_y; if (surface_impl->type_hint == GDK_SURFACE_TYPE_HINT_SPLASHSCREEN) { HMONITOR monitor; MONITORINFO mi; monitor = MonitorFromWindow (GDK_SURFACE_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 (surface_impl->transient_owner != NULL && GDK_SURFACE_IS_MAPPED (surface_impl->transient_owner)) { GdkSurface *owner = surface_impl->transient_owner; /* Center on transient parent */ center_on_rect.left = (owner->x - _gdk_offset_x) * surface_impl->surface_scale; center_on_rect.top = (owner->y - _gdk_offset_y) * surface_impl->surface_scale; center_on_rect.right = center_on_rect.left + owner->width * surface_impl->surface_scale; center_on_rect.bottom = center_on_rect.top + owner->height * surface_impl->surface_scale; _gdk_win32_adjust_client_rect (GDK_SURFACE (owner), ¢er_on_rect); center = TRUE; } if (center) { window_rect.left = 0; window_rect.top = 0; window_rect.right = window->width * surface_impl->surface_scale; window_rect.bottom = window->height * surface_impl->surface_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_SURFACE_HWND (window), SWP_NOZORDER_SPECIFIED, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); } if (!already_mapped && GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL) { /* Ensure new windows are fully onscreen */ RECT window_rect; HMONITOR monitor; MONITORINFO mi; int x, y; GetWindowRect (GDK_SURFACE_HWND (window), &window_rect); monitor = MonitorFromWindow (GDK_SURFACE_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_SURFACE_HWND (window), SWP_NOZORDER_SPECIFIED, window_rect.left, window_rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); } } if (window->state & GDK_SURFACE_STATE_FULLSCREEN) { gdk_surface_fullscreen (window); } else if (window->state & GDK_SURFACE_STATE_MAXIMIZED) { GtkShowWindow (window, SW_MAXIMIZE); } else if (window->state & GDK_SURFACE_STATE_ICONIFIED) { if (focus_on_map) GtkShowWindow (window, SW_RESTORE); else GtkShowWindow (window, SW_SHOWNOACTIVATE); } else if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP || !focus_on_map) { if (!IsWindowVisible (GDK_SURFACE_HWND (window))) GtkShowWindow (window, SW_SHOWNOACTIVATE); else GtkShowWindow (window, SW_SHOWNA); } else if (!IsWindowVisible (GDK_SURFACE_HWND (window))) { GtkShowWindow (window, SW_SHOWNORMAL); } else { GtkShowWindow (window, SW_SHOW); } /* Sync STATE_ABOVE to TOPMOST */ if (GDK_SURFACE_TYPE (window) != GDK_SURFACE_TEMP && (((window->state & GDK_SURFACE_STATE_ABOVE) && !(exstyle & WS_EX_TOPMOST)) || (!(window->state & GDK_SURFACE_STATE_ABOVE) && (exstyle & WS_EX_TOPMOST)))) { API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), (window->state & GDK_SURFACE_STATE_ABOVE)?HWND_TOPMOST:HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)); } } static void gdk_win32_surface_show (GdkSurface *window, gboolean already_mapped) { show_window_internal (window, FALSE, FALSE); } static void gdk_win32_surface_hide (GdkSurface *window) { if (window->destroyed) return; GDK_NOTE (MISC, g_print ("gdk_win32_surface_hide: %p: %s\n", GDK_SURFACE_HWND (window), _gdk_win32_surface_state_to_string (window->state))); if (GDK_SURFACE_IS_MAPPED (window)) gdk_synthesize_surface_state (window, 0, GDK_SURFACE_STATE_WITHDRAWN); _gdk_surface_clear_update_area (window); if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL) ShowOwnedPopups (GDK_SURFACE_HWND (window), FALSE); /* Use SetWindowPos to hide transparent windows so automatic redraws * in other windows can be suppressed. */ if (GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE) & WS_EX_TRANSPARENT) { SetWindowPos (GDK_SURFACE_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_surface_withdraw (GdkSurface *window) { if (window->destroyed) return; GDK_NOTE (MISC, g_print ("gdk_win32_surface_withdraw: %p: %s\n", GDK_SURFACE_HWND (window), _gdk_win32_surface_state_to_string (window->state))); gdk_surface_hide (window); /* ??? */ } static void gdk_win32_surface_move (GdkSurface *window, gint x, gint y) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_win32_surface_move: %p: %+d%+d\n", GDK_SURFACE_HWND (window), x, y)); if (window->state & GDK_SURFACE_STATE_FULLSCREEN) return; /* Don't check GDK_SURFACE_TYPE (window) == GDK_SURFACE_CHILD. * Foreign windows (another app's windows) might be children of our * windows! Especially in the case of gtkplug/socket. */ if (GetAncestor (GDK_SURFACE_HWND (window), GA_PARENT) != GetDesktopWindow ()) { _gdk_surface_move_resize_child (window, x, y, window->width, window->height); } else { RECT outer_rect; GdkSurfaceImplWin32 *impl = GDK_SURFACE_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_SURFACE_HWND (window), (x - _gdk_offset_x) * impl->surface_scale, (y - _gdk_offset_y) * impl->surface_scale)); API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), SWP_NOZORDER_SPECIFIED, (x - _gdk_offset_x) * impl->surface_scale, (y - _gdk_offset_y) * impl->surface_scale, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); } } static void gdk_win32_surface_resize (GdkSurface *window, gint width, gint height) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; if (width < 1) width = 1; if (height < 1) height = 1; GDK_NOTE (MISC, g_print ("gdk_win32_surface_resize: %p: %dx%d\n", GDK_SURFACE_HWND (window), width, height)); if (window->state & GDK_SURFACE_STATE_FULLSCREEN) return; if (GetAncestor (GDK_SURFACE_HWND (window), GA_PARENT) != GetDesktopWindow ()) { _gdk_surface_move_resize_child (window, window->x, window->y, width, height); } else { RECT outer_rect; 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_SURFACE_HWND (window), outer_rect.right - outer_rect.left, outer_rect.bottom - outer_rect.top)); API_CALL (SetWindowPos, (GDK_SURFACE_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_surface_move_resize_internal (GdkSurface *window, gint x, gint y, gint width, gint height) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; if (width < 1) width = 1; if (height < 1) height = 1; if (window->state & GDK_SURFACE_STATE_FULLSCREEN) return; GDK_NOTE (MISC, g_print ("gdk_win32_surface_move_resize: %p: %dx%d@%+d%+d\n", GDK_SURFACE_HWND (window), width, height, x, y)); if (GetAncestor (GDK_SURFACE_HWND (window), GA_PARENT) != GetDesktopWindow ()) { _gdk_surface_move_resize_child (window, x, y, width, height); } else { RECT outer_rect; GdkSurfaceImplWin32 *impl = GDK_SURFACE_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_SURFACE_HWND (window), (x - _gdk_offset_x) * impl->surface_scale, (y - _gdk_offset_y) * impl->surface_scale, outer_rect.right - outer_rect.left, outer_rect.bottom - outer_rect.top)); API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), SWP_NOZORDER_SPECIFIED, (x - _gdk_offset_x) * impl->surface_scale, (y - _gdk_offset_y) * impl->surface_scale, outer_rect.right - outer_rect.left, outer_rect.bottom - outer_rect.top, SWP_NOACTIVATE | SWP_NOZORDER)); } } static void gdk_win32_surface_move_resize (GdkSurface *window, gboolean with_move, gint x, gint y, gint width, gint height) { GdkSurfaceImplWin32 *surface_impl; surface_impl = GDK_SURFACE_IMPL_WIN32 (window->impl); surface_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_SURFACE_HWND (window) == _modal_move_resize_window) goto out; if (with_move && (width < 0 && height < 0)) { gdk_win32_surface_move (window, x, y); } else { if (with_move) { gdk_win32_surface_move_resize_internal (window, x, y, width, height); } else { gdk_win32_surface_resize (window, width, height); } } out: surface_impl->inhibit_configure = FALSE; _gdk_win32_emit_configure_event (window); } static void gdk_win32_surface_raise (GdkSurface *window) { if (!GDK_SURFACE_DESTROYED (window)) { GDK_NOTE (MISC, g_print ("gdk_win32_surface_raise: %p\n", GDK_SURFACE_HWND (window))); if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP) API_CALL (SetWindowPos, (GDK_SURFACE_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_SURFACE_HWND (window)); else API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)); } } static void gdk_win32_surface_lower (GdkSurface *window) { if (!GDK_SURFACE_DESTROYED (window)) { GDK_NOTE (MISC, g_print ("gdk_win32_surface_lower: %p\n" "... SetWindowPos(%p,HWND_BOTTOM,0,0,0,0," "NOACTIVATE|NOMOVE|NOSIZE)\n", GDK_SURFACE_HWND (window), GDK_SURFACE_HWND (window))); API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)); } } static void gdk_win32_surface_set_urgency_hint (GdkSurface *window, gboolean urgent) { FLASHWINFO flashwinfo; typedef BOOL (WINAPI *PFN_FlashWindowEx) (FLASHWINFO*); PFN_FlashWindowEx flashWindowEx = NULL; g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; flashWindowEx = (PFN_FlashWindowEx) GetProcAddress (GetModuleHandle ("user32.dll"), "FlashWindowEx"); if (flashWindowEx) { flashwinfo.cbSize = sizeof (flashwinfo); flashwinfo.hwnd = GDK_SURFACE_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_SURFACE_HWND (window), urgent); } } static gboolean get_effective_window_decorations (GdkSurface *window, GdkWMDecoration *decoration) { GdkSurfaceImplWin32 *impl; impl = (GdkSurfaceImplWin32 *)window->impl; if (gdk_surface_get_decorations (window, decoration)) return TRUE; if (window->surface_type != GDK_SURFACE_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_SURFACE_TYPE_HINT_DIALOG || impl->type_hint == GDK_SURFACE_TYPE_HINT_MENU || impl->type_hint == GDK_SURFACE_TYPE_HINT_TOOLBAR) { *decoration |= GDK_DECOR_MINIMIZE; } else if (impl->type_hint == GDK_SURFACE_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_SURFACE_TYPE_HINT_DIALOG || impl->type_hint == GDK_SURFACE_TYPE_HINT_MENU || impl->type_hint == GDK_SURFACE_TYPE_HINT_TOOLBAR) { *decoration |= GDK_DECOR_MINIMIZE; } return TRUE; } else { switch (impl->type_hint) { case GDK_SURFACE_TYPE_HINT_DIALOG: *decoration = (GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); return TRUE; case GDK_SURFACE_TYPE_HINT_MENU: *decoration = (GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); return TRUE; case GDK_SURFACE_TYPE_HINT_TOOLBAR: case GDK_SURFACE_TYPE_HINT_UTILITY: gdk_surface_set_skip_taskbar_hint (window, TRUE); gdk_surface_set_skip_pager_hint (window, TRUE); *decoration = (GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); return TRUE; case GDK_SURFACE_TYPE_HINT_SPLASHSCREEN: *decoration = (GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MENU | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); return TRUE; case GDK_SURFACE_TYPE_HINT_DOCK: return FALSE; case GDK_SURFACE_TYPE_HINT_DESKTOP: return FALSE; default: /* Fall thru */ case GDK_SURFACE_TYPE_HINT_NORMAL: *decoration = GDK_DECOR_ALL; return TRUE; } } return FALSE; } static void gdk_win32_surface_set_geometry_hints (GdkSurface *window, const GdkGeometry *geometry, GdkSurfaceHints geom_mask) { GdkSurfaceImplWin32 *impl; FullscreenInfo *fi; g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_surface_set_geometry_hints: %p\n", GDK_SURFACE_HWND (window))); impl = GDK_SURFACE_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_surface_update_style_bits (window); } static void gdk_win32_surface_set_title (GdkSurface *window, const gchar *title) { wchar_t *wtitle; g_return_if_fail (GDK_IS_SURFACE (window)); g_return_if_fail (title != NULL); if (GDK_SURFACE_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_surface_set_title: %p: %s\n", GDK_SURFACE_HWND (window), title)); GDK_NOTE (MISC_OR_EVENTS, title = g_strdup_printf ("%p %s", GDK_SURFACE_HWND (window), title)); wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL); API_CALL (SetWindowTextW, (GDK_SURFACE_HWND (window), wtitle)); g_free (wtitle); GDK_NOTE (MISC_OR_EVENTS, g_free ((char *) title)); } static void gdk_win32_surface_set_role (GdkSurface *window, const gchar *role) { g_return_if_fail (GDK_IS_SURFACE (window)); GDK_NOTE (MISC, g_print ("gdk_surface_set_role: %p: %s\n", GDK_SURFACE_HWND (window), (role ? role : "NULL"))); /* XXX */ } static void gdk_win32_surface_set_transient_for (GdkSurface *window, GdkSurface *parent) { HWND window_id, parent_id; LONG_PTR old_ptr; DWORD w32_error; GdkSurfaceImplWin32 *surface_impl = GDK_SURFACE_IMPL_WIN32 (window->impl); GdkSurfaceImplWin32 *parent_impl = NULL; GSList *item; g_return_if_fail (GDK_IS_SURFACE (window)); window_id = GDK_SURFACE_HWND (window); parent_id = parent != NULL ? GDK_SURFACE_HWND (parent) : NULL; GDK_NOTE (MISC, g_print ("gdk_surface_set_transient_for: %p: %p\n", window_id, parent_id)); if (GDK_SURFACE_DESTROYED (window) || (parent && GDK_SURFACE_DESTROYED (parent))) { if (GDK_SURFACE_DESTROYED (window)) GDK_NOTE (MISC, g_print ("... destroyed!\n")); else GDK_NOTE (MISC, g_print ("... owner destroyed!\n")); return; } if (parent == NULL) { GdkSurfaceImplWin32 *trans_impl = GDK_SURFACE_IMPL_WIN32 (surface_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 (surface_impl->transient_owner)); g_object_unref (G_OBJECT (window)); surface_impl->transient_owner = NULL; } else { parent_impl = GDK_SURFACE_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++; surface_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 (GdkSurface *window) { modal_window_stack = g_slist_prepend (modal_window_stack, window); } void _gdk_remove_modal_window (GdkSurface *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 (GdkSurface *window) { GSList *l; gboolean found_any = FALSE; for (l = modal_window_stack; l != NULL; l = l->next) { GdkSurface *modal = l->data; if (modal == window) return FALSE; if (GDK_SURFACE_IS_MAPPED (modal)) found_any = TRUE; } return found_any; } GdkSurface * _gdk_modal_current (void) { GSList *l; for (l = modal_window_stack; l != NULL; l = l->next) { GdkSurface *modal = l->data; if (GDK_SURFACE_IS_MAPPED (modal)) return modal; } return NULL; } static void gdk_win32_surface_get_geometry (GdkSurface *window, gint *x, gint *y, gint *width, gint *height) { if (!GDK_SURFACE_DESTROYED (window)) { RECT rect; GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); API_CALL (GetClientRect, (GDK_SURFACE_HWND (window), &rect)); POINT pt; GdkSurface *parent = gdk_surface_get_parent (window); pt.x = rect.left; pt.y = rect.top; ClientToScreen (GDK_SURFACE_HWND (window), &pt); if (parent) ScreenToClient (GDK_SURFACE_HWND (parent), &pt); rect.left = pt.x; rect.top = pt.y; pt.x = rect.right; pt.y = rect.bottom; ClientToScreen (GDK_SURFACE_HWND (window), &pt); if (parent) ScreenToClient (GDK_SURFACE_HWND (parent), &pt); rect.right = pt.x; rect.bottom = pt.y; if (parent == NULL) { rect.left += _gdk_offset_x * impl->surface_scale; rect.top += _gdk_offset_y * impl->surface_scale; rect.right += _gdk_offset_x * impl->surface_scale; rect.bottom += _gdk_offset_y * impl->surface_scale; } if (x) *x = rect.left / impl->surface_scale; if (y) *y = rect.top / impl->surface_scale; if (width) *width = (rect.right - rect.left) / impl->surface_scale; if (height) *height = (rect.bottom - rect.top) / impl->surface_scale; GDK_NOTE (MISC, g_print ("gdk_win32_surface_get_geometry: %p: %ldx%ld@%+ld%+ld\n", GDK_SURFACE_HWND (window), (rect.right - rect.left) / impl->surface_scale, (rect.bottom - rect.top) / impl->surface_scale, rect.left, rect.top)); } } static void gdk_win32_surface_get_root_coords (GdkSurface *window, gint x, gint y, gint *root_x, gint *root_y) { gint tx; gint ty; POINT pt; GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); pt.x = x * impl->surface_scale; pt.y = y * impl->surface_scale; ClientToScreen (GDK_SURFACE_HWND (window), &pt); tx = pt.x; ty = pt.y; if (root_x) *root_x = (tx + _gdk_offset_x) / impl->surface_scale; if (root_y) *root_y = (ty + _gdk_offset_y) / impl->surface_scale; GDK_NOTE (MISC, g_print ("gdk_win32_surface_get_root_coords: %p: %+d%+d %+d%+d\n", GDK_SURFACE_HWND (window), x * impl->surface_scale, y * impl->surface_scale, (tx + _gdk_offset_x) / impl->surface_scale, (ty + _gdk_offset_y) / impl->surface_scale)); } static void gdk_win32_surface_restack_toplevel (GdkSurface *window, GdkSurface *sibling, gboolean above) { // ### TODO } static void gdk_win32_surface_get_frame_extents (GdkSurface *window, GdkRectangle *rect) { HWND hwnd; RECT r; GdkSurfaceImplWin32 *impl; g_return_if_fail (GDK_IS_SURFACE (window)); g_return_if_fail (rect != NULL); rect->x = 0; rect->y = 0; rect->width = 1; rect->height = 1; if (GDK_SURFACE_DESTROYED (window)) return; /* FIXME: window is documented to be a toplevel GdkSurface, so is it really * necessary to walk its parent chain? */ while (window->parent && window->parent->parent) window = window->parent; impl = GDK_SURFACE_IMPL_WIN32 (window->impl); hwnd = GDK_SURFACE_HWND (window); API_CALL (GetWindowRect, (hwnd, &r)); /* Initialize to real, unscaled size */ rect->x = r.left + _gdk_offset_x * impl->surface_scale; rect->y = r.top + _gdk_offset_y * impl->surface_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->surface_scale + impl->surface_scale - 1) / impl->surface_scale; rect->height = (rect->height + rect->y % impl->surface_scale + impl->surface_scale - 1) / impl->surface_scale; rect->x = r.left / impl->surface_scale + _gdk_offset_x; rect->y = r.top / impl->surface_scale + _gdk_offset_y; GDK_NOTE (MISC, g_print ("gdk_surface_get_frame_extents: %p: %dx%d@%+d%+d\n", GDK_SURFACE_HWND (window), rect->width, rect->height, rect->x, rect->y)); } static gboolean gdk_surface_win32_get_device_state (GdkSurface *window, GdkDevice *device, gdouble *x, gdouble *y, GdkModifierType *mask) { GdkSurface *child; g_return_val_if_fail (window == NULL || GDK_IS_SURFACE (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 void gdk_win32_surface_set_accept_focus (GdkSurface *window, gboolean accept_focus) { g_return_if_fail (GDK_IS_SURFACE (window)); accept_focus = accept_focus != FALSE; if (window->accept_focus != accept_focus) window->accept_focus = accept_focus; } static void gdk_win32_surface_set_focus_on_map (GdkSurface *window, gboolean focus_on_map) { g_return_if_fail (GDK_IS_SURFACE (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_surface_set_icon_list (GdkSurface *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; GdkSurfaceImplWin32 *impl; g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window) || textures == NULL) return; impl = GDK_SURFACE_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); small_hicon = _gdk_win32_texture_to_hicon (small_texture); /* Set the icons */ SendMessageW (GDK_SURFACE_HWND (window), WM_SETICON, ICON_BIG, (LPARAM)big_hicon); SendMessageW (GDK_SURFACE_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_surface_set_icon_name (GdkSurface *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_SURFACE (window)); if (GDK_SURFACE_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_SURFACE_HWND (window), name)); #endif } static GdkSurface * gdk_win32_surface_get_group (GdkSurface *window) { g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); if (GDK_SURFACE_DESTROYED (window)) return NULL; g_warning ("gdk_surface_get_group not yet implemented"); return NULL; } static void gdk_win32_surface_set_group (GdkSurface *window, GdkSurface *leader) { g_return_if_fail (GDK_IS_SURFACE (window)); g_return_if_fail (leader == NULL || GDK_IS_SURFACE (leader)); if (GDK_SURFACE_DESTROYED (window) || GDK_SURFACE_DESTROYED (leader)) return; g_warning ("gdk_surface_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_surface_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_surface_lacks_wm_decorations (GdkSurface *window) { GdkSurfaceImplWin32 *impl; LONG style; gboolean has_any_decorations; if (GDK_SURFACE_DESTROYED (window)) return FALSE; impl = GDK_SURFACE_IMPL_WIN32 (window->impl); /* This is because GTK calls gdk_surface_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_SURFACE_HWND (window) == 0) return FALSE; style = GetWindowLong (GDK_SURFACE_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_SURFACE_HWND (window), w32_error)); return FALSE; } /* Keep this in sync with _gdk_win32_surface_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_SURFACE_HWND (window), style)); return !has_any_decorations; } void _gdk_win32_surface_update_style_bits (GdkSurface *window) { GdkSurfaceImplWin32 *impl = (GdkSurfaceImplWin32 *)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_SURFACE_STATE_FULLSCREEN) return; old_style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE); old_exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE); GetClientRect (GDK_SURFACE_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->surface_type == GDK_SURFACE_TEMP) { new_exstyle |= WS_EX_TOOLWINDOW; will_be_topmost = TRUE; } else if (impl->type_hint == GDK_SURFACE_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_surface_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_surface_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_surface_update_style_bits: %p: no change\n", GDK_SURFACE_HWND (window))); return; } if (old_style != new_style) { GDK_NOTE (MISC, g_print ("_gdk_win32_surface_update_style_bits: %p: STYLE: %s => %s\n", GDK_SURFACE_HWND (window), _gdk_win32_surface_style_to_string (old_style), _gdk_win32_surface_style_to_string (new_style))); SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, new_style); } if (old_exstyle != new_exstyle) { GDK_NOTE (MISC, g_print ("_gdk_win32_surface_update_style_bits: %p: EXSTYLE: %s => %s\n", GDK_SURFACE_HWND (window), _gdk_win32_surface_exstyle_to_string (old_exstyle), _gdk_win32_surface_exstyle_to_string (new_exstyle))); SetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE, new_exstyle); } AdjustWindowRectEx (&after, new_style, FALSE, new_exstyle); GetWindowRect (GDK_SURFACE_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_SURFACE_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 (GdkSurface *window) { GdkWMFunction functions; BOOL all; if (_gdk_surface_get_functions (window, &functions)) { HMENU hmenu = GetSystemMenu (GDK_SURFACE_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_surface_set_decorations (GdkSurface *window, GdkWMDecoration decorations) { GdkSurfaceImplWin32 *impl; g_return_if_fail (GDK_IS_SURFACE (window)); impl = GDK_SURFACE_IMPL_WIN32 (window->impl); GDK_NOTE (MISC, g_print ("gdk_surface_set_decorations: %p: %s %s%s%s%s%s%s\n", GDK_SURFACE_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_surface_update_style_bits (window); } static gboolean gdk_win32_surface_get_decorations (GdkSurface *window, GdkWMDecoration *decorations) { GdkSurfaceImplWin32 *impl; g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); impl = GDK_SURFACE_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-surface-functions"); return quark; } static void gdk_win32_surface_set_functions (GdkSurface *window, GdkWMFunction functions) { GdkWMFunction* functions_copy; g_return_if_fail (GDK_IS_SURFACE (window)); GDK_NOTE (MISC, g_print ("gdk_surface_set_functions: %p: %s %s%s%s%s%s\n", GDK_SURFACE_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_surface_get_functions (GdkSurface *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; GdkSurfaceImplWin32 *impl = GDK_SURFACE_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->surface_scale; trigger_thickness = AEROSNAP_REGION_TRIGGER_THICKNESS * impl->surface_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 (GdkSurface *window) { GdkSurfaceImplWin32 *impl; impl = GDK_SURFACE_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 (GdkSurface *window, GdkMonitor *monitor) { GdkSurfaceImplWin32 *impl; GdkRectangle rect; impl = GDK_SURFACE_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_surface_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 (GdkSurface *window, GdkSurfaceImplWin32 *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_SURFACE_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_SURFACE_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->surface_scale, (hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top) / impl->surface_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->surface_scale, (hmonitor_info.rcMonitor.bottom - hmonitor_info.rcMonitor.top) / impl->surface_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->surface_scale, (placement.rcNormalPosition.bottom - placement.rcNormalPosition.top) / impl->surface_scale, placement.rcNormalPosition.left, placement.rcNormalPosition.top)); width = (placement.rcNormalPosition.right - placement.rcNormalPosition.left) / impl->surface_scale; height = (placement.rcNormalPosition.bottom - placement.rcNormalPosition.top) / impl->surface_scale; x = (placement.rcNormalPosition.left - hmonitor_info.rcMonitor.left) / impl->surface_scale; y = (placement.rcNormalPosition.top - hmonitor_info.rcMonitor.top) / impl->surface_scale; wwidth = (hmonitor_info.rcWork.right - hmonitor_info.rcWork.left) / impl->surface_scale; wheight = (hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top) / impl->surface_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 (GdkSurface *window) { SHORT maxysize; gint x, y; gint width, height; GdkSurfaceImplWin32 *impl; impl = GDK_SURFACE_IMPL_WIN32 (window->impl); impl->snap_state = GDK_WIN32_AEROSNAP_STATE_FULLUP; stash_window (window, impl); maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN) / impl->surface_scale; gdk_surface_get_position (window, &x, &y); width = gdk_surface_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_surface_move_resize (window, x, y, width, height); } static void snap_left (GdkSurface *window, GdkMonitor *monitor, GdkMonitor *snap_monitor) { GdkRectangle rect; GdkSurfaceImplWin32 *impl; impl = GDK_SURFACE_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_surface_move_resize (window, rect.x, rect.y, rect.width, rect.height); } static void snap_right (GdkSurface *window, GdkMonitor *monitor, GdkMonitor *snap_monitor) { GdkRectangle rect; GdkSurfaceImplWin32 *impl; impl = GDK_SURFACE_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_surface_move_resize (window, rect.x, rect.y, rect.width, rect.height); } void _gdk_win32_surface_handle_aerosnap (GdkSurface *window, GdkWin32AeroSnapCombo combo) { GdkSurfaceImplWin32 *impl; GdkDisplay *display; gint n_monitors; GdkSurfaceState surface_state = gdk_surface_get_state (window); gboolean minimized = surface_state & GDK_SURFACE_STATE_ICONIFIED; gboolean maximized = surface_state & GDK_SURFACE_STATE_MAXIMIZED; gboolean halfsnapped; GdkMonitor *monitor; impl = GDK_SURFACE_IMPL_WIN32 (window->impl); display = gdk_surface_get_display (window); n_monitors = gdk_display_get_n_monitors (display); monitor = gdk_display_get_monitor_at_surface (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_surface_maximize (window); } break; case GDK_WIN32_AEROSNAP_COMBO_DOWN: case GDK_WIN32_AEROSNAP_COMBO_SHIFTDOWN: if (maximized) { gdk_surface_unmaximize (window); unsnap (window, monitor); } else if (halfsnapped) unsnap (window, monitor); else if (!minimized) gdk_surface_iconify (window); break; case GDK_WIN32_AEROSNAP_COMBO_LEFT: if (maximized) gdk_surface_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_surface_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 (GdkSurface *window, GdkWin32AeroSnapState snap) { GdkMonitor *monitor; GdkDisplay *display; display = gdk_surface_get_display (window); monitor = gdk_display_get_monitor_at_surface (display, window); switch (snap) { case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED: break; case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE: unsnap (window, monitor); gdk_surface_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 GdkSurface-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_dll_hinstance; 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"gdkSurfaceDumb"; 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_dll_hinstance, 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; GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (context->window->impl); line_width = AEROSNAP_INDICATOR_LINE_WIDTH * impl->surface_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_SURFACE_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_SURFACE_EDGE_NORTH: current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height); break; case GDK_SURFACE_EDGE_WEST: current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width); break; case GDK_SURFACE_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_SURFACE_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_SURFACE_EDGE_SOUTH_EAST: current_rect.x = context->indicator_target.x; current_rect.y = context->indicator_target.y; break; case GDK_SURFACE_EDGE_SOUTH: current_rect.y = context->indicator_target.y; break; case GDK_SURFACE_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->surface_scale, (current_rect.y - context->indicator_window_rect.y) * impl->surface_scale, current_rect.width * impl->surface_scale, current_rect.height * impl->surface_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; GdkSurfaceImplWin32 *impl; gboolean do_source_remove = FALSE; indicator_opacity = AEROSNAP_INDICATOR_OPACITY; if (GDK_SURFACE_DESTROYED (context->window) || !ensure_snap_indicator_exists (context)) { do_source_remove = TRUE; } impl = GDK_SURFACE_IMPL_WIN32 (context->window->impl); if (!ensure_snap_indicator_surface (context, context->indicator_window_rect.width, context->indicator_window_rect.height, impl->surface_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->surface_scale; window_position.y = (context->indicator_window_rect.y - _gdk_offset_y) * impl->surface_scale; window_size.cx = context->indicator_window_rect.width * impl->surface_scale; window_size.cy = context->indicator_window_rect.height * impl->surface_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_SURFACE_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_SURFACE_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 (GdkSurface *window, GdkW32DragMoveResizeContext *context) { SHORT maxysize; GdkRectangle from, to; GdkRectangle to_adjusted, from_adjusted, from_or_to; GdkSurfaceImplWin32 *impl; GDK_NOTE (MISC, g_print ("Update fullup indicator\n")); if (GDK_SURFACE_DESTROYED (context->window)) return; if (context->shape_indicator == NULL) return; impl = GDK_SURFACE_IMPL_WIN32 (window->impl); maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN); gdk_surface_get_position (window, &to.x, &to.y); to.width = gdk_surface_get_width (window); to.height = gdk_surface_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->surface_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->surface_scale); } static void start_indicator (GdkSurface *window, GdkW32DragMoveResizeContext *context, gint x, gint y, GdkWin32AeroSnapState state) { GdkMonitor *monitor; GdkRectangle workarea; SHORT maxysize; GdkRectangle start_size, end_size; GdkDisplay *display; GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); display = gdk_surface_get_display (window); monitor = gdk_display_get_monitor_at_point (display, x, y); gdk_monitor_get_workarea (monitor, &workarea); maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN) / impl->surface_scale; gdk_surface_get_position (window, &start_size.x, &start_size.y); start_size.width = gdk_surface_get_width (window); start_size.height = gdk_surface_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->surface_scale); } static void stop_indicator (GdkSurface *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 (GdkSurface *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; if (context->op == GDK_WIN32_DRAGOP_RESIZE) switch (context->edge) { case GDK_SURFACE_EDGE_NORTH_WEST: case GDK_SURFACE_EDGE_NORTH_EAST: case GDK_SURFACE_EDGE_WEST: case GDK_SURFACE_EDGE_EAST: case GDK_SURFACE_EDGE_SOUTH_WEST: case GDK_SURFACE_EDGE_SOUTH_EAST: break; case GDK_SURFACE_EDGE_SOUTH: case GDK_SURFACE_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, GdkSurfaceEdge edge) { switch (op) { case GDK_WIN32_DRAGOP_MOVE: return "move"; case GDK_WIN32_DRAGOP_RESIZE: switch (edge) { case GDK_SURFACE_EDGE_NORTH_WEST: return "nw-resize"; case GDK_SURFACE_EDGE_NORTH: return "n-resize"; case GDK_SURFACE_EDGE_NORTH_EAST: return "ne-resize"; case GDK_SURFACE_EDGE_WEST: return "w-resize"; case GDK_SURFACE_EDGE_EAST: return "e-resize"; case GDK_SURFACE_EDGE_SOUTH_WEST: return "sw-resize"; case GDK_SURFACE_EDGE_SOUTH: return "s-resize"; case GDK_SURFACE_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 (GdkSurface *window, gdouble x, gdouble y) { return x >= 0 && x < window->width && y >= 0 && y < window->height && (window->input_shape == NULL || cairo_region_contains_point (window->input_shape, x, y)); } static GdkSurface * child_window_at_coordinates (GdkSurface *window, gint root_x, gint root_y) { gint x, y; GList *l; GList *children; children = gdk_surface_peek_children (window); gdk_surface_get_root_origin (window, &x, &y); x = root_x - x; y = root_y - y; for (l = children; l; l = g_list_next (l)) { GdkSurface *child = GDK_SURFACE (l->data); if (point_in_window (child, x, y)) return child; } return window; } static void setup_drag_move_resize_context (GdkSurface *window, GdkW32DragMoveResizeContext *context, GdkW32WindowDragOp op, GdkSurfaceEdge edge, GdkDevice *device, gint button, gint root_x, gint root_y, guint32 timestamp) { RECT rect; const gchar *cursor_name; GdkSurface *pointer_window; GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); gboolean maximized = gdk_surface_get_state (window) & GDK_SURFACE_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_surface_get_display (window); monitor = gdk_display_get_monitor_at_surface (display, window); gdk_surface_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->surface_scale; swy += impl->margins.top / impl->surface_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_SURFACE_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->surface_scale, placement.rcNormalPosition.top + _gdk_offset_y * impl->surface_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->surface_scale; shadow_unmax_height = unmax_height - impl->margins_y * impl->surface_scale; if (offsetx * impl->surface_scale < (shadow_unmax_width / 2) && offsety * impl->surface_scale < (shadow_unmax_height / 2)) { placement.rcNormalPosition.top = (root_y - offsety + impl->margins.top - _gdk_offset_y) * impl->surface_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->surface_scale; placement.rcNormalPosition.right = placement.rcNormalPosition.left + unmax_width; } else { placement.rcNormalPosition.right = (root_x + offsetx + impl->margins.right - _gdk_offset_x) * impl->surface_scale; placement.rcNormalPosition.left = placement.rcNormalPosition.right - unmax_width; } } else { placement.rcNormalPosition.left = (root_x * impl->surface_scale) - (unmax_width / 2) - (_gdk_offset_x * impl->surface_scale); if (offsety * impl->surface_scale < shadow_unmax_height / 2) placement.rcNormalPosition.top = (root_y - offsety + impl->margins.top - _gdk_offset_y) * impl->surface_scale; else placement.rcNormalPosition.top = (root_y * impl->surface_scale) - (unmax_height / 2) - (_gdk_offset_y * impl->surface_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->surface_scale, placement.rcNormalPosition.top + _gdk_offset_y * impl->surface_scale)); API_CALL (SetWindowPlacement, (GDK_SURFACE_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->surface_scale; if (left_half) new_pos.x = root_x - offsetx + impl->margins.left / impl->surface_scale; else new_pos.x = root_x + offsetx + impl->margins.left / impl->surface_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_surface_move_resize (window, new_pos.x, new_pos.y, new_pos.width, new_pos.height); } if (maximized) gdk_surface_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_surface_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_surface_get_toplevel (window), context->op, context->edge, context->device, context->button, context->start_root_x, context->start_root_y, context->timestamp)); } void gdk_win32_surface_end_move_resize_drag (GdkSurface *window) { GdkSurfaceImplWin32 *impl = GDK_SURFACE_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_surface_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 (GdkSurface *window, RECT *window_rect, SIZE *window_size, POINT *window_position) { GdkSurfaceImplWin32 *impl; impl = GDK_SURFACE_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->surface_scale; window_rect->right -= _gdk_offset_x * impl->surface_scale; window_rect->top -= _gdk_offset_y * impl->surface_scale; window_rect->bottom -= _gdk_offset_y * impl->surface_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; } void _gdk_win32_update_layered_window_from_cache (GdkSurface *surface, RECT *client_rect, gboolean do_move, gboolean do_resize, gboolean do_paint) { POINT window_position; SIZE window_size; BLENDFUNCTION blender; HDC hdc; SIZE *window_size_ptr; POINT source_point = { 0, 0 }; POINT *source_point_ptr; GdkSurfaceImplWin32 *impl; impl = GDK_SURFACE_IMPL_WIN32 (surface->impl); gdk_win32_get_window_size_and_position_from_client_rect (surface, 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; /* Strictly speaking, we don't need to supply hdc, source_point and * window_size to just move the window. 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. */ hdc = cairo_win32_surface_get_dc (impl->cache_surface); window_size_ptr = &window_size; source_point_ptr = &source_point; if (gdk_display_is_composited (gdk_surface_get_display (surface))) { if (!do_paint) hdc = NULL; if (!do_resize) window_size_ptr = NULL; if (!do_move) source_point_ptr = NULL; } /* Don't use UpdateLayeredWindow on minimized windows */ if (IsIconic (GDK_SURFACE_HWND (surface))) API_CALL (SetWindowPos, (GDK_SURFACE_HWND (surface), SWP_NOZORDER_SPECIFIED, window_position.x, window_position.y, window_size.cx, window_size.cy, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW)); else API_CALL (UpdateLayeredWindow, (GDK_SURFACE_HWND (surface), NULL, &window_position, window_size_ptr, hdc, source_point_ptr, 0, &blender, ULW_ALPHA)); } void gdk_win32_surface_do_move_resize_drag (GdkSurface *window, gint x, gint y) { RECT rect; RECT new_rect; gint diffy, diffx; MINMAXINFO mmi; GdkSurfaceImplWin32 *impl; GdkW32DragMoveResizeContext *context; gint width; gint height; impl = GDK_SURFACE_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->surface_scale; diffy = (y - context->start_root_y) * impl->surface_scale; switch (context->op) { case GDK_WIN32_DRAGOP_RESIZE: switch (context->edge) { case GDK_SURFACE_EDGE_NORTH_WEST: new_rect.left += diffx; new_rect.top += diffy; break; case GDK_SURFACE_EDGE_NORTH: new_rect.top += diffy; break; case GDK_SURFACE_EDGE_NORTH_EAST: new_rect.right += diffx; new_rect.top += diffy; break; case GDK_SURFACE_EDGE_WEST: new_rect.left += diffx; break; case GDK_SURFACE_EDGE_EAST: new_rect.right += diffx; break; case GDK_SURFACE_EDGE_SOUTH_WEST: new_rect.left += diffx; new_rect.bottom += diffy; break; case GDK_SURFACE_EDGE_SOUTH: new_rect.bottom += diffy; break; case GDK_SURFACE_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_surface_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_SURFACE_EDGE_NORTH_WEST: case GDK_SURFACE_EDGE_WEST: case GDK_SURFACE_EDGE_SOUTH_WEST: new_rect.left = new_rect.right - mmi.ptMaxTrackSize.x; break; case GDK_SURFACE_EDGE_NORTH_EAST: case GDK_SURFACE_EDGE_EAST: case GDK_SURFACE_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_SURFACE_EDGE_NORTH_WEST: case GDK_SURFACE_EDGE_WEST: case GDK_SURFACE_EDGE_SOUTH_WEST: new_rect.left = new_rect.right - mmi.ptMinTrackSize.x; break; case GDK_SURFACE_EDGE_NORTH_EAST: case GDK_SURFACE_EDGE_EAST: case GDK_SURFACE_EDGE_SOUTH_EAST: default: new_rect.right = new_rect.left + mmi.ptMinTrackSize.x; break; } } if (height > mmi.ptMaxTrackSize.y) { switch (context->edge) { case GDK_SURFACE_EDGE_NORTH_WEST: case GDK_SURFACE_EDGE_NORTH: case GDK_SURFACE_EDGE_NORTH_EAST: new_rect.top = new_rect.bottom - mmi.ptMaxTrackSize.y; case GDK_SURFACE_EDGE_SOUTH_WEST: case GDK_SURFACE_EDGE_SOUTH: case GDK_SURFACE_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_SURFACE_EDGE_NORTH_WEST: case GDK_SURFACE_EDGE_NORTH: case GDK_SURFACE_EDGE_NORTH_EAST: new_rect.top = new_rect.bottom - mmi.ptMinTrackSize.y; case GDK_SURFACE_EDGE_SOUTH_WEST: case GDK_SURFACE_EDGE_SOUTH: case GDK_SURFACE_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, TRUE, FALSE, FALSE); } 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_SURFACE_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_surface_begin_resize_drag (GdkSurface *window, GdkSurfaceEdge edge, GdkDevice *device, gint button, gint root_x, gint root_y, guint32 timestamp) { GdkSurfaceImplWin32 *impl; g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window) || IsIconic (GDK_SURFACE_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_SURFACE_IMPL_WIN32 (window->impl); if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE) gdk_win32_surface_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_surface_begin_move_drag (GdkSurface *window, GdkDevice *device, gint button, gint root_x, gint root_y, guint32 timestamp) { GdkSurfaceImplWin32 *impl; g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window) || IsIconic (GDK_SURFACE_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_SURFACE_IMPL_WIN32 (window->impl); if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE) gdk_win32_surface_end_move_resize_drag (window); setup_drag_move_resize_context (window, &impl->drag_move_resize_context, GDK_WIN32_DRAGOP_MOVE, GDK_SURFACE_EDGE_NORTH_WEST, device, button, root_x, root_y, timestamp); } /* * Setting window states */ static void gdk_win32_surface_iconify (GdkSurface *window) { HWND old_active_window; g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_surface_iconify: %p: %s\n", GDK_SURFACE_HWND (window), _gdk_win32_surface_state_to_string (window->state))); if (GDK_SURFACE_IS_MAPPED (window)) { old_active_window = GetActiveWindow (); GtkShowWindow (window, SW_MINIMIZE); if (old_active_window != GDK_SURFACE_HWND (window)) SetActiveWindow (old_active_window); } else { gdk_synthesize_surface_state (window, 0, GDK_SURFACE_STATE_ICONIFIED); } } static void gdk_win32_surface_deiconify (GdkSurface *window) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_surface_deiconify: %p: %s\n", GDK_SURFACE_HWND (window), _gdk_win32_surface_state_to_string (window->state))); if (GDK_SURFACE_IS_MAPPED (window)) { show_window_internal (window, GDK_SURFACE_IS_MAPPED (window), TRUE); } else { gdk_synthesize_surface_state (window, GDK_SURFACE_STATE_ICONIFIED, 0); } } static void gdk_win32_surface_stick (GdkSurface *window) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; /* FIXME: Do something? */ } static void gdk_win32_surface_unstick (GdkSurface *window) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; /* FIXME: Do something? */ } static void gdk_win32_surface_maximize (GdkSurface *window) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_surface_maximize: %p: %s\n", GDK_SURFACE_HWND (window), _gdk_win32_surface_state_to_string (window->state))); if (GDK_SURFACE_IS_MAPPED (window)) GtkShowWindow (window, SW_MAXIMIZE); else gdk_synthesize_surface_state (window, 0, GDK_SURFACE_STATE_MAXIMIZED); } static void gdk_win32_surface_unmaximize (GdkSurface *window) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_surface_unmaximize: %p: %s\n", GDK_SURFACE_HWND (window), _gdk_win32_surface_state_to_string (window->state))); if (GDK_SURFACE_IS_MAPPED (window)) GtkShowWindow (window, SW_RESTORE); else gdk_synthesize_surface_state (window, GDK_SURFACE_STATE_MAXIMIZED, 0); } static void gdk_win32_surface_fullscreen (GdkSurface *window) { gint x, y, width, height; FullscreenInfo *fi; HMONITOR monitor; MONITORINFO mi; g_return_if_fail (GDK_IS_SURFACE (window)); fi = g_new (FullscreenInfo, 1); if (!GetWindowRect (GDK_SURFACE_HWND (window), &(fi->r))) g_free (fi); else { GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); monitor = MonitorFromWindow (GDK_SURFACE_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_SURFACE_HWND (window), GWL_STYLE); /* Send state change before configure event */ gdk_synthesize_surface_state (window, 0, GDK_SURFACE_STATE_FULLSCREEN); SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, (fi->style & ~WS_OVERLAPPEDWINDOW) | WS_POPUP); API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_TOP, x, y, width, height, SWP_NOCOPYBITS | SWP_SHOWWINDOW)); } } static void gdk_win32_surface_unfullscreen (GdkSurface *window) { FullscreenInfo *fi; g_return_if_fail (GDK_IS_SURFACE (window)); fi = g_object_get_data (G_OBJECT (window), "fullscreen-info"); if (fi) { GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); gdk_synthesize_surface_state (window, GDK_SURFACE_STATE_FULLSCREEN, 0); impl->hint_flags = fi->hint_flags; SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, fi->style); API_CALL (SetWindowPos, (GDK_SURFACE_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_surface_update_style_bits (window); } } static void gdk_win32_surface_set_keep_above (GdkSurface *window, gboolean setting) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_surface_set_keep_above: %p: %s\n", GDK_SURFACE_HWND (window), setting ? "YES" : "NO")); if (GDK_SURFACE_IS_MAPPED (window)) { API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), setting ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)); } gdk_synthesize_surface_state (window, setting ? GDK_SURFACE_STATE_BELOW : GDK_SURFACE_STATE_ABOVE, setting ? GDK_SURFACE_STATE_ABOVE : 0); } static void gdk_win32_surface_set_keep_below (GdkSurface *window, gboolean setting) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_surface_set_keep_below: %p: %s\n", GDK_SURFACE_HWND (window), setting ? "YES" : "NO")); if (GDK_SURFACE_IS_MAPPED (window)) { API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), setting ? HWND_BOTTOM : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)); } gdk_synthesize_surface_state (window, setting ? GDK_SURFACE_STATE_ABOVE : GDK_SURFACE_STATE_BELOW, setting ? GDK_SURFACE_STATE_BELOW : 0); } static void gdk_win32_surface_focus (GdkSurface *window, guint32 timestamp) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_surface_focus: %p: %s\n", GDK_SURFACE_HWND (window), _gdk_win32_surface_state_to_string (window->state))); if (window->state & GDK_SURFACE_STATE_MAXIMIZED) GtkShowWindow (window, SW_SHOWMAXIMIZED); else if (window->state & GDK_SURFACE_STATE_ICONIFIED) GtkShowWindow (window, SW_RESTORE); else if (!IsWindowVisible (GDK_SURFACE_HWND (window))) GtkShowWindow (window, SW_SHOWNORMAL); else GtkShowWindow (window, SW_SHOW); SetFocus (GDK_SURFACE_HWND (window)); } static void gdk_win32_surface_set_modal_hint (GdkSurface *window, gboolean modal) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_surface_set_modal_hint: %p: %s\n", GDK_SURFACE_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_SURFACE_IS_MAPPED (window)) API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), modal ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)); #else if (modal) { _gdk_push_modal_window (window); gdk_surface_raise (window); } else { _gdk_remove_modal_window (window); } #endif } static void gdk_win32_surface_set_skip_taskbar_hint (GdkSurface *window, gboolean skips_taskbar) { static GdkSurface *owner = NULL; //GdkSurfaceAttr wa; g_return_if_fail (GDK_IS_SURFACE (window)); GDK_NOTE (MISC, g_print ("gdk_surface_set_skip_taskbar_hint: %p: %s, doing nothing\n", GDK_SURFACE_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.surface_type = GDK_SURFACE_TEMP; wa.wclass = GDK_INPUT_OUTPUT; wa.width = wa.height = 1; owner = gdk_surface_new_internal (NULL, &wa, 0, TRUE); } #endif SetWindowLongPtr (GDK_SURFACE_HWND (window), GWLP_HWNDPARENT, (LONG_PTR) GDK_SURFACE_HWND (owner)); #if 0 /* Should we also turn off the minimize and maximize buttons? */ SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE) & ~(WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_SYSMENU)); SetWindowPos (GDK_SURFACE_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_SURFACE_HWND (window), GWLP_HWNDPARENT, 0); } } static void gdk_win32_surface_set_skip_pager_hint (GdkSurface *window, gboolean skips_pager) { g_return_if_fail (GDK_IS_SURFACE (window)); GDK_NOTE (MISC, g_print ("gdk_surface_set_skip_pager_hint: %p: %s, doing nothing\n", GDK_SURFACE_HWND (window), skips_pager ? "YES" : "NO")); } static void gdk_win32_surface_set_type_hint (GdkSurface *window, GdkSurfaceTypeHint hint) { g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; GDK_NOTE (MISC, G_STMT_START{ static GEnumClass *class = NULL; if (!class) class = g_type_class_ref (GDK_TYPE_SURFACE_TYPE_HINT); g_print ("gdk_surface_set_type_hint: %p: %s\n", GDK_SURFACE_HWND (window), g_enum_get_value (class, hint)->value_name); }G_STMT_END); ((GdkSurfaceImplWin32 *)window->impl)->type_hint = hint; _gdk_win32_surface_update_style_bits (window); } static GdkSurfaceTypeHint gdk_win32_surface_get_type_hint (GdkSurface *window) { g_return_val_if_fail (GDK_IS_SURFACE (window), GDK_SURFACE_TYPE_HINT_NORMAL); if (GDK_SURFACE_DESTROYED (window)) return GDK_SURFACE_TYPE_HINT_NORMAL; return GDK_SURFACE_IMPL_WIN32 (window->impl)->type_hint; } GdkSurface * gdk_win32_surface_lookup_for_display (GdkDisplay *display, HWND anid) { g_return_val_if_fail (display == gdk_display_get_default (), NULL); return (GdkSurface*) gdk_win32_handle_table_lookup (anid); } static void gdk_win32_surface_set_opacity (GdkSurface *window, gdouble opacity) { LONG exstyle; typedef BOOL (WINAPI *PFN_SetLayeredWindowAttributes) (HWND, COLORREF, BYTE, DWORD); PFN_SetLayeredWindowAttributes setLayeredWindowAttributes = NULL; GdkSurfaceImplWin32 *impl; g_return_if_fail (GDK_IS_SURFACE (window)); if (GDK_SURFACE_DESTROYED (window)) return; if (opacity < 0) opacity = 0; else if (opacity > 1) opacity = 1; impl = GDK_SURFACE_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->surface_scale, &window_rect); _gdk_win32_update_layered_window_from_cache (window, &window_rect, TRUE, TRUE, TRUE); } return; } exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE); if (!(exstyle & WS_EX_LAYERED)) SetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE, exstyle | WS_EX_LAYERED); setLayeredWindowAttributes = (PFN_SetLayeredWindowAttributes)GetProcAddress (GetModuleHandle ("user32.dll"), "SetLayeredWindowAttributes"); if (setLayeredWindowAttributes) { API_CALL (setLayeredWindowAttributes, (GDK_SURFACE_HWND (window), 0, opacity * 0xff, LWA_ALPHA)); } } gboolean gdk_win32_surface_is_win32 (GdkSurface *window) { return GDK_SURFACE_IS_WIN32 (window); } static gboolean gdk_win32_surface_show_window_menu (GdkSurface *window, GdkEvent *event) { double event_x, event_y; gint x, y; GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); switch (event->any.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_SURFACE_HWND (window), WM_SYSMENU, 0, MAKELPARAM (x * impl->surface_scale, y * impl->surface_scale)); return TRUE; } HWND gdk_win32_surface_get_impl_hwnd (GdkSurface *window) { if (GDK_SURFACE_IS_WIN32 (window)) return GDK_SURFACE_HWND (window); return NULL; } BOOL WINAPI GtkShowWindow (GdkSurface *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_SURFACE_HWND (window); GdkSurfaceImplWin32 *impl = GDK_SURFACE_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->surface_scale, impl->surface_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_surface_set_shadow_width (GdkSurface *window, gint left, gint right, gint top, gint bottom) { GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); if (GDK_SURFACE_DESTROYED (window)) return; GDK_NOTE (MISC, g_print ("gdk_win32_surface_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->surface_scale; impl->margins.top = top; impl->margins.bottom = bottom * impl->surface_scale; impl->margins_x = left + right; impl->margins_y = top + bottom; } gint _gdk_win32_surface_get_scale_factor (GdkSurface *window) { GdkDisplay *display; GdkSurfaceImplWin32 *impl; GdkWin32Display *win32_display; if (GDK_SURFACE_DESTROYED (window)) return 1; g_return_val_if_fail (window != NULL, 1); display = gdk_surface_get_display (window); impl = GDK_SURFACE_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->surface_scale = win32_display->surface_scale; else impl->surface_scale = _gdk_win32_display_get_monitor_scale_factor (win32_display, NULL, GDK_SURFACE_HWND (window), NULL); return impl->surface_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_surface_get_unscaled_size (GdkSurface *window, gint *unscaled_width, gint *unscaled_height) { GdkSurfaceImplWin32 *impl = GDK_SURFACE_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 (GdkSurface *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_surface_impl_win32_class_init (GdkSurfaceImplWin32Class *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->dispose = gdk_surface_impl_win32_dispose; object_class->finalize = gdk_surface_impl_win32_finalize; impl_class->show = gdk_win32_surface_show; impl_class->hide = gdk_win32_surface_hide; impl_class->withdraw = gdk_win32_surface_withdraw; impl_class->raise = gdk_win32_surface_raise; impl_class->lower = gdk_win32_surface_lower; impl_class->restack_toplevel = gdk_win32_surface_restack_toplevel; impl_class->move_resize = gdk_win32_surface_move_resize; impl_class->get_geometry = gdk_win32_surface_get_geometry; impl_class->get_device_state = gdk_surface_win32_get_device_state; impl_class->get_root_coords = gdk_win32_surface_get_root_coords; impl_class->input_shape_combine_region = gdk_win32_input_shape_combine_region; impl_class->destroy = gdk_win32_surface_destroy; //impl_class->beep = gdk_x11_surface_beep; impl_class->show_window_menu = gdk_win32_surface_show_window_menu; impl_class->focus = gdk_win32_surface_focus; impl_class->set_type_hint = gdk_win32_surface_set_type_hint; impl_class->get_type_hint = gdk_win32_surface_get_type_hint; impl_class->set_modal_hint = gdk_win32_surface_set_modal_hint; impl_class->set_skip_taskbar_hint = gdk_win32_surface_set_skip_taskbar_hint; impl_class->set_skip_pager_hint = gdk_win32_surface_set_skip_pager_hint; impl_class->set_urgency_hint = gdk_win32_surface_set_urgency_hint; impl_class->set_geometry_hints = gdk_win32_surface_set_geometry_hints; impl_class->set_title = gdk_win32_surface_set_title; impl_class->set_role = gdk_win32_surface_set_role; //impl_class->set_startup_id = gdk_x11_surface_set_startup_id; impl_class->set_transient_for = gdk_win32_surface_set_transient_for; impl_class->get_frame_extents = gdk_win32_surface_get_frame_extents; impl_class->set_accept_focus = gdk_win32_surface_set_accept_focus; impl_class->set_focus_on_map = gdk_win32_surface_set_focus_on_map; impl_class->set_icon_list = gdk_win32_surface_set_icon_list; impl_class->set_icon_name = gdk_win32_surface_set_icon_name; impl_class->iconify = gdk_win32_surface_iconify; impl_class->deiconify = gdk_win32_surface_deiconify; impl_class->stick = gdk_win32_surface_stick; impl_class->unstick = gdk_win32_surface_unstick; impl_class->maximize = gdk_win32_surface_maximize; impl_class->unmaximize = gdk_win32_surface_unmaximize; impl_class->fullscreen = gdk_win32_surface_fullscreen; impl_class->unfullscreen = gdk_win32_surface_unfullscreen; impl_class->set_keep_above = gdk_win32_surface_set_keep_above; impl_class->set_keep_below = gdk_win32_surface_set_keep_below; impl_class->get_group = gdk_win32_surface_get_group; impl_class->set_group = gdk_win32_surface_set_group; impl_class->set_decorations = gdk_win32_surface_set_decorations; impl_class->get_decorations = gdk_win32_surface_get_decorations; impl_class->set_functions = gdk_win32_surface_set_functions; impl_class->set_shadow_width = gdk_win32_surface_set_shadow_width; impl_class->begin_resize_drag = gdk_win32_surface_begin_resize_drag; impl_class->begin_move_drag = gdk_win32_surface_begin_move_drag; impl_class->set_opacity = gdk_win32_surface_set_opacity; impl_class->destroy_notify = gdk_win32_surface_destroy_notify; impl_class->register_dnd = _gdk_win32_surface_register_dnd; impl_class->drag_begin = _gdk_win32_surface_drag_begin; impl_class->create_gl_context = _gdk_win32_surface_create_gl_context; impl_class->get_scale_factor = _gdk_win32_surface_get_scale_factor; impl_class->get_unscaled_size = _gdk_win32_surface_get_unscaled_size; } HGDIOBJ gdk_win32_surface_get_handle (GdkSurface *window) { if (!GDK_SURFACE_IS_WIN32 (window)) { g_warning (G_STRLOC " window is not a native Win32 window"); return NULL; } return GDK_SURFACE_HWND (window); }