1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 1998-2004 Tor Lillqvist
4  * Copyright (C) 2001-2011 Hans Breuer
5  * Copyright (C) 2007-2009 Cody Russell
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
26  */
27 
28 #include "config.h"
29 #include <stdlib.h>
30 
31 #include "gdk.h"
32 #include "gdksurfaceprivate.h"
33 #include "gdktoplevelprivate.h"
34 #include "gdkpopupprivate.h"
35 #include "gdkdragsurfaceprivate.h"
36 #include "gdkprivate-win32.h"
37 #include "gdkdeviceprivate.h"
38 #include "gdkdevicemanager-win32.h"
39 #include "gdkenumtypes.h"
40 #include "gdkwin32.h"
41 #include "gdkdisplayprivate.h"
42 #include "gdkframeclockidleprivate.h"
43 #include "gdkmonitorprivate.h"
44 #include "gdkwin32surface.h"
45 #include "gdkwin32cursor.h"
46 #include "gdkinput-winpointer.h"
47 #include "gdkglcontext-win32.h"
48 #include "gdkdisplay-win32.h"
49 #include "gdkdevice-win32.h"
50 #include "gdkcairocontext-win32.h"
51 
52 #include <cairo-win32.h>
53 #include <dwmapi.h>
54 #include <math.h>
55 
56 static void gdk_surface_win32_finalize (GObject *object);
57 static void compute_toplevel_size      (GdkSurface *surface,
58                                         gboolean    update_geometry,
59                                         int        *width,
60                                         int        *height);
61 
62 static gpointer parent_class = NULL;
63 static GSList *modal_window_stack = NULL;
64 
65 typedef struct _FullscreenInfo FullscreenInfo;
66 
67 struct _FullscreenInfo
68 {
69   RECT  r;
70   guint hint_flags;
71   LONG  style;
72 };
73 
74 struct _AeroSnapEdgeRegion
75 {
76   /* The rectangle along the edge of the desktop
77    * that allows application of the snap transformation.
78    */
79   GdkRectangle edge;
80 
81   /* A subregion of the "edge". When the pointer hits
82    * this region, the transformation is revealed.
83    * Usually it is 1-pixel thick and is located at the
84    * very edge of the screen. When there's a toolbar
85    * at that edge, the "trigger" and the "edge" regions
86    * are extended to cover that toolbar.
87    */
88   GdkRectangle trigger;
89 };
90 
91 typedef struct _AeroSnapEdgeRegion AeroSnapEdgeRegion;
92 
93 /* Size of the regions at the edges of the desktop where
94  * snapping can take place (in pixels)
95  */
96 #define AEROSNAP_REGION_THICKNESS (20)
97 /* Size of the subregions that actually trigger the snapping prompt
98  * (in pixels).
99  */
100 #define AEROSNAP_REGION_TRIGGER_THICKNESS (1)
101 
102 /* The gap between the snap indicator and the edge of the work area
103  * (in pixels).
104  */
105 #define AEROSNAP_INDICATOR_EDGE_GAP (10)
106 
107 /* Width of the outline of the snap indicator
108  * (in pixels).
109  */
110 #define AEROSNAP_INDICATOR_LINE_WIDTH (3.0)
111 
112 /* Corner radius of the snap indicator.
113  */
114 #define AEROSNAP_INDICATOR_CORNER_RADIUS (3.0)
115 
116 /* The time it takes for snap indicator to expand/shrink
117  * from current window size to future position of the
118  * snapped window (in microseconds).
119  */
120 #define AEROSNAP_INDICATOR_ANIMATION_DURATION (200 * 1000)
121 
122 /* Opacity if the snap indicator. */
123 #define AEROSNAP_INDICATOR_OPACITY (0.5)
124 
125 /* The interval between snap indicator redraws (in milliseconds).
126  * 16 is ~ 1/60 of a second, for ~60 FPS.
127  */
128 #define AEROSNAP_INDICATOR_ANIMATION_TICK (16)
129 
130 static void     gdk_win32_impl_frame_clock_after_paint (GdkFrameClock *clock,
131                                                         GdkSurface    *surface);
132 
133 G_DEFINE_TYPE (GdkWin32Surface, gdk_win32_surface, GDK_TYPE_SURFACE)
134 
135 GType gdk_win32_toplevel_get_type (void) G_GNUC_CONST;
136 GType gdk_win32_popup_get_type (void) G_GNUC_CONST;
137 GType gdk_win32_drag_surface_get_type (void) G_GNUC_CONST;
138 
139 #define GDK_TYPE_WIN32_TOPLEVEL (gdk_win32_toplevel_get_type ())
140 #define GDK_TYPE_WIN32_POPUP (gdk_win32_popup_get_type ())
141 #define GDK_TYPE_WIN32_DRAG_SURFACE (gdk_win32_drag_surface_get_type ())
142 
143 static void
gdk_win32_surface_init(GdkWin32Surface * impl)144 gdk_win32_surface_init (GdkWin32Surface *impl)
145 {
146   impl->hicon_big = NULL;
147   impl->hicon_small = NULL;
148   impl->hint_flags = 0;
149   impl->transient_owner = NULL;
150   impl->transient_children = NULL;
151   impl->num_transients = 0;
152   impl->changing_state = FALSE;
153   impl->surface_scale = 1;
154 }
155 
156 
157 static void
gdk_surface_win32_dispose(GObject * object)158 gdk_surface_win32_dispose (GObject *object)
159 {
160   GdkWin32Surface *surface;
161 
162   g_return_if_fail (GDK_IS_WIN32_SURFACE (object));
163 
164   surface = GDK_WIN32_SURFACE (object);
165 
166   g_clear_object (&surface->cursor);
167 
168   G_OBJECT_CLASS (parent_class)->dispose (object);
169 }
170 
171 
172 static void
gdk_surface_win32_finalize(GObject * object)173 gdk_surface_win32_finalize (GObject *object)
174 {
175   GdkWin32Surface *surface;
176 
177   g_return_if_fail (GDK_IS_WIN32_SURFACE (object));
178 
179   surface = GDK_WIN32_SURFACE (object);
180 
181   if (!GDK_SURFACE_DESTROYED (surface))
182     {
183       gdk_win32_handle_table_remove (surface->handle);
184     }
185 
186   g_clear_pointer (&surface->snap_stash, g_free);
187   g_clear_pointer (&surface->snap_stash_int, g_free);
188 
189   if (surface->hicon_big != NULL)
190     {
191       GDI_CALL (DestroyIcon, (surface->hicon_big));
192       surface->hicon_big = NULL;
193     }
194 
195   if (surface->hicon_small != NULL)
196     {
197       GDI_CALL (DestroyIcon, (surface->hicon_small));
198       surface->hicon_small = NULL;
199     }
200 
201   if (surface->cache_surface)
202     {
203       cairo_surface_destroy (surface->cache_surface);
204       surface->cache_surface = NULL;
205     }
206 
207   _gdk_win32_surface_unregister_dnd (GDK_SURFACE (surface));
208 
209   g_assert (surface->transient_owner == NULL);
210   g_assert (surface->transient_children == NULL);
211 
212   G_OBJECT_CLASS (parent_class)->finalize (object);
213 }
214 
215 void
_gdk_win32_get_window_client_area_rect(GdkSurface * window,int scale,RECT * rect)216 _gdk_win32_get_window_client_area_rect (GdkSurface *window,
217                                         int         scale,
218                                         RECT       *rect)
219 {
220   int x, y, width, height;
221 
222   gdk_surface_get_geometry (window, &x, &y, NULL, NULL);
223   width = gdk_surface_get_width (window);
224   height = gdk_surface_get_height (window);
225   rect->left = x * scale;
226   rect->top = y * scale;
227   rect->right = rect->left + width * scale;
228   rect->bottom = rect->top + height * scale;
229 }
230 
231 static void
gdk_win32_impl_frame_clock_after_paint(GdkFrameClock * clock,GdkSurface * surface)232 gdk_win32_impl_frame_clock_after_paint (GdkFrameClock *clock,
233                                         GdkSurface    *surface)
234 {
235   DWM_TIMING_INFO timing_info;
236   LARGE_INTEGER tick_frequency;
237   GdkFrameTimings *timings;
238 
239   timings = gdk_frame_clock_get_timings (clock, gdk_frame_clock_get_frame_counter (clock));
240 
241   if (timings)
242     {
243       timings->refresh_interval = 16667; /* default to 1/60th of a second */
244       timings->presentation_time = 0;
245 
246       if (QueryPerformanceFrequency (&tick_frequency))
247         {
248           HRESULT hr;
249 
250           timing_info.cbSize = sizeof (timing_info);
251           hr = DwmGetCompositionTimingInfo (NULL, &timing_info);
252 
253           if (SUCCEEDED (hr))
254             {
255               timings->refresh_interval = timing_info.qpcRefreshPeriod * (double)G_USEC_PER_SEC / tick_frequency.QuadPart;
256               timings->presentation_time = timing_info.qpcCompose * (double)G_USEC_PER_SEC / tick_frequency.QuadPart;
257             }
258         }
259 
260       timings->complete = TRUE;
261     }
262 }
263 
264 void
_gdk_win32_adjust_client_rect(GdkSurface * window,RECT * rect)265 _gdk_win32_adjust_client_rect (GdkSurface *window,
266 			       RECT      *rect)
267 {
268   LONG style, exstyle;
269 
270   style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE);
271   exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE);
272   API_CALL (AdjustWindowRectEx, (rect, style, FALSE, exstyle));
273 }
274 
275 gboolean
_gdk_win32_surface_enable_transparency(GdkSurface * window)276 _gdk_win32_surface_enable_transparency (GdkSurface *window)
277 {
278   DWM_BLURBEHIND blur_behind;
279   HRGN empty_region;
280   HRESULT call_result;
281   HWND thiswindow;
282 
283   if (window == NULL || GDK_SURFACE_HWND (window) == NULL)
284     return FALSE;
285 
286   if (!gdk_display_is_composited (gdk_surface_get_display (window)))
287     return FALSE;
288 
289   thiswindow = GDK_SURFACE_HWND (window);
290 
291   empty_region = CreateRectRgn (0, 0, -1, -1);
292 
293   if (empty_region == NULL)
294     return FALSE;
295 
296   memset (&blur_behind, 0, sizeof (blur_behind));
297   blur_behind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
298   blur_behind.hRgnBlur = empty_region;
299   blur_behind.fEnable = TRUE;
300   call_result = DwmEnableBlurBehindWindow (thiswindow, &blur_behind);
301 
302   if (!SUCCEEDED (call_result))
303     g_warning ("%s: %s (%p) failed: %" G_GINT32_MODIFIER "x",
304         G_STRLOC, "DwmEnableBlurBehindWindow", thiswindow, (guint32) call_result);
305 
306   DeleteObject (empty_region);
307 
308   return SUCCEEDED (call_result);
309 }
310 
311 static const char *
get_default_title(void)312 get_default_title (void)
313 {
314   const char *title;
315   title = g_get_application_name ();
316   if (!title)
317     title = g_get_prgname ();
318 
319   return title;
320 }
321 
322 /* RegisterGdkClass
323  *   is a wrapper function for RegisterWindowClassEx.
324  *   It creates at least one unique class for every
325  *   GdkSurfaceType. If support for single window-specific icons
326  *   is ever needed (e.g Dialog specific), every such window should
327  *   get its own class
328  */
329 static ATOM
RegisterGdkClass(GdkSurfaceType wtype)330 RegisterGdkClass (GdkSurfaceType wtype)
331 {
332   static ATOM klassTOPLEVEL = 0;
333   static ATOM klassTEMP     = 0;
334   static HICON hAppIcon = NULL;
335   static HICON hAppIconSm = NULL;
336   static WNDCLASSEXW wcl;
337   ATOM klass = 0;
338 
339   wcl.cbSize = sizeof (WNDCLASSEX);
340   wcl.style = 0; /* DON'T set CS_<H,V>REDRAW. It causes total redraw
341                   * on WM_SIZE and WM_MOVE. Flicker, Performance!
342                   */
343   wcl.lpfnWndProc = _gdk_win32_surface_procedure;
344   wcl.cbClsExtra = 0;
345   wcl.cbWndExtra = 0;
346   wcl.hInstance = _gdk_dll_hinstance;
347   wcl.hIcon = 0;
348   wcl.hIconSm = 0;
349 
350   /* initialize once! */
351   if (0 == hAppIcon && 0 == hAppIconSm)
352     {
353       char sLoc [MAX_PATH+1];
354 
355       // try to load first icon of executable program
356       if (0 != GetModuleFileName (NULL, sLoc, MAX_PATH))
357         {
358           ExtractIconEx (sLoc, 0, &hAppIcon, &hAppIconSm, 1);
359 
360           if (0 == hAppIcon && 0 == hAppIconSm)
361             {
362               // fallback : load icon from GTK DLL
363               if (0 != GetModuleFileName (_gdk_dll_hinstance, sLoc, MAX_PATH))
364 		{
365 		  ExtractIconEx (sLoc, 0, &hAppIcon, &hAppIconSm, 1);
366 		}
367             }
368         }
369 
370       if (0 == hAppIcon && 0 == hAppIconSm)
371         {
372           hAppIcon = LoadImage (NULL, IDI_APPLICATION, IMAGE_ICON,
373                                 GetSystemMetrics (SM_CXICON),
374                                 GetSystemMetrics (SM_CYICON), 0);
375           hAppIconSm = LoadImage (NULL, IDI_APPLICATION, IMAGE_ICON,
376                                   GetSystemMetrics (SM_CXSMICON),
377                                   GetSystemMetrics (SM_CYSMICON), 0);
378         }
379     }
380 
381   if (0 == hAppIcon)
382     hAppIcon = hAppIconSm;
383   else if (0 == hAppIconSm)
384     hAppIconSm = hAppIcon;
385 
386   wcl.lpszMenuName = NULL;
387 
388   /* initialize once per class */
389   /*
390    * HB: Setting the background brush leads to flicker, because we
391    * don't get asked how to clear the background. This is not what
392    * we want, at least not for input_only windows ...
393    */
394 #define ONCE_PER_CLASS() \
395   wcl.hIcon = CopyIcon (hAppIcon); \
396   wcl.hIconSm = CopyIcon (hAppIconSm); \
397   wcl.hbrBackground = NULL; \
398   wcl.hCursor = LoadCursor (NULL, IDC_ARROW);
399 
400   /* MSDN: CS_OWNDC is needed for OpenGL contexts */
401   wcl.style |= CS_OWNDC;
402 
403   switch (wtype)
404     {
405     case GDK_SURFACE_TOPLEVEL:
406     case GDK_SURFACE_POPUP:
407       if (0 == klassTOPLEVEL)
408         {
409           wcl.lpszClassName = L"gdkSurfaceToplevel";
410 
411           ONCE_PER_CLASS ();
412           klassTOPLEVEL = RegisterClassExW (&wcl);
413         }
414       klass = klassTOPLEVEL;
415       break;
416 
417     case GDK_SURFACE_TEMP:
418       if (klassTEMP == 0)
419         {
420           wcl.lpszClassName = L"gdkSurfaceTemp";
421           wcl.style |= CS_SAVEBITS;
422           ONCE_PER_CLASS ();
423           klassTEMP = RegisterClassExW (&wcl);
424         }
425 
426       klass = klassTEMP;
427 
428       break;
429 
430     default:
431       g_assert_not_reached ();
432       break;
433     }
434 
435   if (klass == 0)
436     {
437       WIN32_API_FAILED ("RegisterClassExW");
438       g_error ("That is a fatal error");
439     }
440   return klass;
441 }
442 
443 /*
444  * Create native windows.
445  *
446  * With the default Gdk the created windows are mostly toplevel windows.
447  *
448  * Placement of the window is derived from the passed in window,
449  * except for toplevel window where OS/Window Manager placement
450  * is used.
451  *
452  * [1] http://mail.gnome.org/archives/gtk-devel-list/2010-August/msg00214.html
453  */
454 GdkSurface *
_gdk_win32_display_create_surface(GdkDisplay * display,GdkSurfaceType surface_type,GdkSurface * parent,int x,int y,int width,int height)455 _gdk_win32_display_create_surface (GdkDisplay     *display,
456                                    GdkSurfaceType  surface_type,
457                                    GdkSurface     *parent,
458                                    int             x,
459                                    int             y,
460                                    int             width,
461                                    int             height)
462 {
463   HWND hwndNew;
464   HANDLE owner;
465   ATOM klass = 0;
466   DWORD dwStyle = 0, dwExStyle;
467   RECT rect;
468   GdkWin32Surface *impl;
469   GdkWin32Display *display_win32;
470   GdkSurface *surface;
471   const char *title;
472   wchar_t *wtitle;
473   int window_width, window_height;
474   int window_x, window_y;
475   int offset_x = 0, offset_y = 0;
476   int real_x = 0, real_y = 0;
477   GdkFrameClock *frame_clock;
478 
479   g_return_val_if_fail (display == _gdk_display, NULL);
480 
481   GDK_NOTE (MISC,
482             g_print ("_gdk_surface_new: %s\n", (surface_type == GDK_SURFACE_TOPLEVEL ? "TOPLEVEL" :
483                                                        (surface_type == GDK_SURFACE_TEMP ? "TEMP" :
484                                                       (surface_type == GDK_SURFACE_TEMP ? "POPUP" : "???")))));
485 
486   display_win32 = GDK_WIN32_DISPLAY (display);
487 
488   if (parent)
489     frame_clock = g_object_ref (gdk_surface_get_frame_clock (parent));
490   else
491     frame_clock = _gdk_frame_clock_idle_new ();
492 
493   switch (surface_type)
494     {
495     case GDK_SURFACE_TOPLEVEL:
496       impl = g_object_new (GDK_TYPE_WIN32_TOPLEVEL,
497                            "display", display,
498                            "frame-clock", frame_clock,
499                            NULL);
500       break;
501     case GDK_SURFACE_POPUP:
502       impl = g_object_new (GDK_TYPE_WIN32_POPUP,
503                            "parent", parent,
504                            "display", display,
505                            "frame-clock", frame_clock,
506                            NULL);
507       break;
508     case GDK_SURFACE_TEMP:
509       impl = g_object_new (GDK_TYPE_WIN32_DRAG_SURFACE,
510                            "display", display,
511                            "frame-clock", frame_clock,
512                            NULL);
513       break;
514     default:
515       g_assert_not_reached ();
516       break;
517     }
518 
519   surface = GDK_SURFACE (impl);
520   surface->x = x;
521   surface->y = y;
522   surface->width = width;
523   surface->height = height;
524 
525   impl->surface_scale = gdk_win32_display_get_monitor_scale_factor (display_win32, NULL, NULL);
526 
527   dwExStyle = 0;
528   owner = NULL;
529 
530   offset_x = _gdk_offset_x;
531   offset_y = _gdk_offset_y;
532   /* MSDN: We need WS_CLIPCHILDREN and WS_CLIPSIBLINGS for GL Context Creation */
533   dwStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
534 
535   switch (surface_type)
536     {
537     case GDK_SURFACE_TOPLEVEL:
538       dwStyle |= WS_OVERLAPPEDWINDOW;
539       break;
540 
541     case GDK_SURFACE_TEMP:
542       dwExStyle |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
543       /* fall through */
544     case GDK_SURFACE_POPUP:
545       dwStyle |= WS_POPUP;
546 
547       /* Only popup and temp windows are fit to use the Owner Window mechanism */
548       if (parent != NULL)
549         owner = GDK_SURFACE_HWND (parent);
550       break;
551 
552     default:
553       g_assert_not_reached ();
554     }
555 
556   rect.left = x * impl->surface_scale;
557   rect.top = y * impl->surface_scale;
558   rect.right = rect.left + width * impl->surface_scale;
559   rect.bottom = rect.top + height * impl->surface_scale;
560 
561   AdjustWindowRectEx (&rect, dwStyle, FALSE, dwExStyle);
562 
563   real_x = (x - offset_x) * impl->surface_scale;
564   real_y = (y - offset_y) * impl->surface_scale;
565 
566   if (surface_type == GDK_SURFACE_TOPLEVEL)
567     {
568       /* We initially place it at default so that we can get the
569          default window positioning if we want */
570       window_x = window_y = CW_USEDEFAULT;
571     }
572   else
573     {
574       /* TEMP: Put these where requested */
575       window_x = real_x;
576       window_y = real_y;
577     }
578 
579   window_width = rect.right - rect.left;
580   window_height = rect.bottom - rect.top;
581 
582   title = get_default_title ();
583   if (!title || !*title)
584     title = "";
585 
586   klass = RegisterGdkClass (surface_type);
587 
588   wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL);
589 
590   hwndNew = CreateWindowExW (dwExStyle,
591 			     MAKEINTRESOURCEW (klass),
592 			     wtitle,
593 			     dwStyle,
594 			     window_x, window_y,
595 			     window_width, window_height,
596 			     owner,
597 			     NULL,
598 			     _gdk_dll_hinstance,
599 			     surface);
600   impl->handle = hwndNew;
601 
602   GetWindowRect (hwndNew, &rect);
603   impl->initial_x = rect.left;
604   impl->initial_y = rect.top;
605 
606   /* Now we know the initial position, move to actually specified position */
607   if (real_x != window_x || real_y != window_y)
608     {
609       API_CALL (SetWindowPos, (hwndNew,
610                 SWP_NOZORDER_SPECIFIED,
611                 real_x, real_y, 0, 0,
612                 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER));
613     }
614 
615   g_object_ref (impl);
616   /* Take note: we're inserting a pointer into a heap-allocated
617    * object (impl). Inserting a pointer to a stack variable
618    * will break the logic, since stack variables are short-lived.
619    * We insert a pointer to the handle instead of the handle itself
620    * probably because we need to hash them differently depending
621    * on the bitness of the OS. That pointer is still unique,
622    * so this works out in the end.
623    */
624   gdk_win32_handle_table_insert (&GDK_SURFACE_HWND (impl), impl);
625 
626   GDK_NOTE (MISC, g_print ("... \"%s\" %dx%d@%+d%+d %p = %p\n",
627 			   title,
628 			   window_width, window_height,
629 			   surface->x - offset_x,
630 			   surface->y - offset_y,
631 			   owner,
632 			   hwndNew));
633 
634   g_free (wtitle);
635 
636   if (impl->handle == NULL)
637     {
638       WIN32_API_FAILED ("CreateWindowExW");
639       g_object_unref (impl);
640       return NULL;
641     }
642 
643   if (display_win32->tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
644     gdk_winpointer_initialize_surface (surface);
645 
646   _gdk_win32_surface_enable_transparency (surface);
647   _gdk_win32_surface_register_dnd (surface);
648   _gdk_win32_surface_update_style_bits (surface);
649 
650   g_signal_connect (frame_clock,
651                     "after-paint",
652                     G_CALLBACK (gdk_win32_impl_frame_clock_after_paint),
653                     impl);
654 
655   g_object_unref (frame_clock);
656   impl->hdc = GetDC (impl->handle);
657 
658   return surface;
659 }
660 
661 static void
662 gdk_win32_surface_set_transient_for (GdkSurface *window,
663 			      GdkSurface *parent);
664 
665 static void
gdk_win32_surface_destroy(GdkSurface * window,gboolean foreign_destroy)666 gdk_win32_surface_destroy (GdkSurface *window,
667 			   gboolean   foreign_destroy)
668 {
669   GdkWin32Surface *surface = GDK_WIN32_SURFACE (window);
670 
671   g_return_if_fail (GDK_IS_SURFACE (window));
672 
673   GDK_NOTE (MISC, g_print ("gdk_win32_surface_destroy: %p\n",
674 			   GDK_SURFACE_HWND (window)));
675 
676   /* Remove ourself from the modal stack */
677   _gdk_remove_modal_window (window);
678 
679   g_signal_handlers_disconnect_by_func (gdk_surface_get_frame_clock (window),
680                                         gdk_win32_impl_frame_clock_after_paint,
681                                         window);
682 
683   /* Remove all our transient children */
684   while (surface->transient_children != NULL)
685     {
686       GdkSurface *child = surface->transient_children->data;
687       gdk_win32_surface_set_transient_for (child, NULL);
688     }
689 
690 #ifdef GDK_WIN32_ENABLE_EGL
691   GdkWin32Display *display = GDK_WIN32_DISPLAY (gdk_surface_get_display (window));
692 
693   /* Get rid of any EGLSurfaces that we might have created */
694   if (surface->egl_surface != EGL_NO_SURFACE)
695     {
696       eglDestroySurface (display->egl_disp, surface->egl_surface);
697       surface->egl_surface = EGL_NO_SURFACE;
698     }
699   if (surface->egl_dummy_surface != EGL_NO_SURFACE)
700     {
701       eglDestroySurface (display->egl_disp, surface->egl_dummy_surface);
702       surface->egl_dummy_surface = EGL_NO_SURFACE;
703     }
704 #endif
705 
706   /* Remove ourself from our transient owner */
707   if (surface->transient_owner != NULL)
708     {
709       gdk_win32_surface_set_transient_for (window, NULL);
710     }
711 
712   if (!foreign_destroy)
713     {
714       window->destroyed = TRUE;
715       DestroyWindow (GDK_SURFACE_HWND (window));
716     }
717 }
718 
719 /* This function is called when the window really gone.
720  */
721 static void
gdk_win32_surface_destroy_notify(GdkSurface * window)722 gdk_win32_surface_destroy_notify (GdkSurface *window)
723 {
724   g_return_if_fail (GDK_IS_SURFACE (window));
725 
726   GDK_NOTE (EVENTS,
727 	    g_print ("gdk_surface_destroy_notify: %p%s\n",
728 		     GDK_SURFACE_HWND (window),
729 		     (GDK_SURFACE_DESTROYED (window) ? " (destroyed)" : "")));
730 
731   if (!GDK_SURFACE_DESTROYED (window))
732     {
733       g_warning ("window %p unexpectedly destroyed",
734                  GDK_SURFACE_HWND (window));
735 
736       _gdk_surface_destroy (window, TRUE);
737     }
738 
739   gdk_win32_handle_table_remove (GDK_SURFACE_HWND (window));
740   g_object_unref (window);
741 }
742 
743 static void
get_outer_rect(GdkSurface * window,int width,int height,RECT * rect)744 get_outer_rect (GdkSurface *window,
745 		int        width,
746 		int        height,
747 		RECT      *rect)
748 {
749   GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
750 
751   rect->left = rect->top = 0;
752   rect->right = width * impl->surface_scale;
753   rect->bottom = height * impl->surface_scale;
754 
755   _gdk_win32_adjust_client_rect (window, rect);
756 }
757 
758 static void
759 gdk_win32_surface_fullscreen (GdkSurface *window);
760 
761 static void
show_window_internal(GdkSurface * window,gboolean already_mapped,gboolean unminimize)762 show_window_internal (GdkSurface *window,
763                       gboolean    already_mapped,
764 		      gboolean    unminimize)
765 {
766   GdkWin32Surface *surface;
767   DWORD exstyle;
768 
769   if (window->destroyed)
770     return;
771 
772   GDK_NOTE (MISC, g_print ("show_window_internal: %p: %s%s\n",
773 			   GDK_SURFACE_HWND (window),
774 			   _gdk_win32_surface_state_to_string (window->state),
775 			   (unminimize ? " unminimize" : "")));
776 
777   /* If asked to show (not unminimize) a withdrawn and iconified
778    * window, do that.
779    */
780   if (!unminimize &&
781       !already_mapped &&
782       (window->state & GDK_TOPLEVEL_STATE_MINIMIZED))
783     {
784       GtkShowWindow (window, SW_SHOWMINNOACTIVE);
785       return;
786     }
787 
788   /* If asked to just show an iconified window, do nothing. */
789   if (!unminimize && (window->state & GDK_TOPLEVEL_STATE_MINIMIZED))
790     return;
791 
792   /* If asked to unminimize an already noniconified window, do
793    * nothing. (Especially, don't cause the window to rise and
794    * activate. There are different calls for that.)
795    */
796   if (unminimize && !(window->state & GDK_TOPLEVEL_STATE_MINIMIZED))
797     return;
798 
799   /* If asked to show (but not raise) a window that is already
800    * visible, do nothing.
801    */
802   if (!unminimize && !already_mapped && IsWindowVisible (GDK_SURFACE_HWND (window)))
803     return;
804 
805   /* Other cases */
806 
807   exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE);
808 
809   /* Use SetWindowPos to show transparent windows so automatic redraws
810    * in other windows can be suppressed.
811    */
812   if (exstyle & WS_EX_TRANSPARENT)
813     {
814       UINT flags = SWP_SHOWWINDOW | SWP_NOREDRAW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER;
815 
816       if (GDK_IS_DRAG_SURFACE (window))
817 	flags |= SWP_NOACTIVATE;
818 
819       SetWindowPos (GDK_SURFACE_HWND (window),
820 		    SWP_NOZORDER_SPECIFIED, 0, 0, 0, 0, flags);
821 
822       return;
823     }
824 
825   /* For initial map of "normal" windows we want to emulate WM window
826    * positioning behaviour, which means:
827    * + default to the initial CW_USEDEFAULT placement,
828    *   no matter if the user moved the window before showing it.
829    * + Certain window types and hints have more elaborate positioning
830    *   schemes.
831    */
832   surface = GDK_WIN32_SURFACE (window);
833   if (!already_mapped &&
834       GDK_IS_TOPLEVEL (window))
835     {
836       gboolean center = FALSE;
837       RECT window_rect, center_on_rect;
838       int x, y;
839 
840       x = surface->initial_x;
841       y = surface->initial_y;
842 
843       if (FALSE)
844 	{
845 	  HMONITOR monitor;
846 	  MONITORINFO mi;
847 
848 	  monitor = MonitorFromWindow (GDK_SURFACE_HWND (window), MONITOR_DEFAULTTONEAREST);
849 	  mi.cbSize = sizeof (mi);
850 	  if (monitor && GetMonitorInfo (monitor, &mi))
851 	    center_on_rect = mi.rcMonitor;
852 	  else
853 	    {
854 	      center_on_rect.left = 0;
855 	      center_on_rect.top = 0;
856 	      center_on_rect.right = GetSystemMetrics (SM_CXSCREEN);
857 	      center_on_rect.bottom = GetSystemMetrics (SM_CYSCREEN);
858 	    }
859 	  center = TRUE;
860 	}
861       else if (surface->transient_owner != NULL &&
862 	       GDK_SURFACE_IS_MAPPED (surface->transient_owner))
863 	{
864 	  GdkSurface *owner = surface->transient_owner;
865 	  /* Center on transient parent */
866 	  center_on_rect.left = (owner->x - _gdk_offset_x) * surface->surface_scale;
867 	  center_on_rect.top = (owner->y - _gdk_offset_y) * surface->surface_scale;
868 	  center_on_rect.right = center_on_rect.left + owner->width * surface->surface_scale;
869 	  center_on_rect.bottom = center_on_rect.top + owner->height * surface->surface_scale;
870 
871 	  _gdk_win32_adjust_client_rect (GDK_SURFACE (owner), &center_on_rect);
872 	  center = TRUE;
873 	}
874 
875       if (center)
876 	{
877 	  window_rect.left = 0;
878 	  window_rect.top = 0;
879 	  window_rect.right = window->width * surface->surface_scale;
880 	  window_rect.bottom = window->height * surface->surface_scale;
881 	  _gdk_win32_adjust_client_rect (window, &window_rect);
882 
883 	  x = center_on_rect.left + ((center_on_rect.right - center_on_rect.left) - (window_rect.right - window_rect.left)) / 2;
884 	  y = center_on_rect.top + ((center_on_rect.bottom - center_on_rect.top) - (window_rect.bottom - window_rect.top)) / 2;
885 	}
886 
887       API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window),
888 			       SWP_NOZORDER_SPECIFIED,
889 			       x, y, 0, 0,
890 			       SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER));
891     }
892 
893   if (!already_mapped && GDK_IS_TOPLEVEL (window))
894     {
895       /* Ensure new windows are fully onscreen */
896       RECT window_rect;
897       HMONITOR monitor;
898       MONITORINFO mi;
899       int x, y;
900 
901       GetWindowRect (GDK_SURFACE_HWND (window), &window_rect);
902 
903       monitor = MonitorFromWindow (GDK_SURFACE_HWND (window), MONITOR_DEFAULTTONEAREST);
904       mi.cbSize = sizeof (mi);
905       if (monitor && GetMonitorInfo (monitor, &mi))
906 	{
907 	  x = window_rect.left;
908 	  y = window_rect.top;
909 
910 	  if (window_rect.right > mi.rcWork.right)
911 	    {
912 	      window_rect.left -= (window_rect.right - mi.rcWork.right);
913 	      window_rect.right -= (window_rect.right - mi.rcWork.right);
914 	    }
915 
916 	  if (window_rect.bottom > mi.rcWork.bottom)
917 	    {
918 	      window_rect.top -= (window_rect.bottom - mi.rcWork.bottom);
919 	      window_rect.bottom -= (window_rect.bottom - mi.rcWork.bottom);
920 	    }
921 
922 	  if (window_rect.left < mi.rcWork.left)
923 	    {
924 	      window_rect.right += (mi.rcWork.left - window_rect.left);
925 	      window_rect.left += (mi.rcWork.left - window_rect.left);
926 	    }
927 
928 	  if (window_rect.top < mi.rcWork.top)
929 	    {
930 	      window_rect.bottom += (mi.rcWork.top - window_rect.top);
931 	      window_rect.top += (mi.rcWork.top - window_rect.top);
932 	    }
933 
934 	  if (x != window_rect.left || y != window_rect.top)
935 	    API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window),
936 				     SWP_NOZORDER_SPECIFIED,
937 				     window_rect.left, window_rect.top, 0, 0,
938 				     SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER));
939 	}
940     }
941 
942 
943   if (window->state & GDK_TOPLEVEL_STATE_FULLSCREEN)
944     {
945       gdk_win32_surface_fullscreen (window);
946     }
947   else if (window->state & GDK_TOPLEVEL_STATE_MAXIMIZED)
948     {
949       GtkShowWindow (window, SW_MAXIMIZE);
950     }
951   else if (window->state & GDK_TOPLEVEL_STATE_MINIMIZED)
952     {
953       GtkShowWindow (window, SW_RESTORE);
954     }
955   else if (GDK_IS_DRAG_SURFACE (window))
956     {
957       if (!IsWindowVisible (GDK_SURFACE_HWND (window)))
958         GtkShowWindow (window, SW_SHOWNOACTIVATE);
959       else
960         GtkShowWindow (window, SW_SHOWNA);
961     }
962   else if (!IsWindowVisible (GDK_SURFACE_HWND (window)))
963     {
964       GtkShowWindow (window, SW_SHOWNORMAL);
965     }
966   else
967     {
968       GtkShowWindow (window, SW_SHOW);
969     }
970 
971   /* Sync STATE_ABOVE to TOPMOST */
972   if (!GDK_IS_DRAG_SURFACE (window) &&
973       (((window->state & GDK_TOPLEVEL_STATE_ABOVE) &&
974 	!(exstyle & WS_EX_TOPMOST)) ||
975        (!(window->state & GDK_TOPLEVEL_STATE_ABOVE) &&
976 	(exstyle & WS_EX_TOPMOST))))
977     {
978       API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window),
979 			       (window->state & GDK_TOPLEVEL_STATE_ABOVE)?HWND_TOPMOST:HWND_NOTOPMOST,
980 			       0, 0, 0, 0,
981 			       SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE));
982     }
983 }
984 
985 void
gdk_win32_surface_show(GdkSurface * window,gboolean already_mapped)986 gdk_win32_surface_show (GdkSurface *window,
987 		       gboolean already_mapped)
988 {
989   show_window_internal (window, FALSE, FALSE);
990 }
991 
992 static void
gdk_win32_surface_hide(GdkSurface * window)993 gdk_win32_surface_hide (GdkSurface *window)
994 {
995   if (window->destroyed)
996     return;
997 
998   GDK_NOTE (MISC, g_print ("gdk_win32_surface_hide: %p: %s\n",
999 			   GDK_SURFACE_HWND (window),
1000 			   _gdk_win32_surface_state_to_string (window->state)));
1001 
1002   if (GDK_SURFACE_IS_MAPPED (window))
1003     gdk_surface_set_is_mapped (window, FALSE);
1004 
1005   _gdk_surface_clear_update_area (window);
1006 
1007   if (GDK_IS_TOPLEVEL (window))
1008     ShowOwnedPopups (GDK_SURFACE_HWND (window), FALSE);
1009 
1010   /* Use SetWindowPos to hide transparent windows so automatic redraws
1011    * in other windows can be suppressed.
1012    */
1013   if (GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE) & WS_EX_TRANSPARENT)
1014     {
1015       SetWindowPos (GDK_SURFACE_HWND (window), SWP_NOZORDER_SPECIFIED,
1016 		    0, 0, 0, 0,
1017 		    SWP_HIDEWINDOW | SWP_NOREDRAW | SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
1018     }
1019   else
1020     {
1021       GtkShowWindow (window, SW_HIDE);
1022     }
1023 }
1024 
1025 static void
gdk_win32_surface_do_move(GdkSurface * window,int x,int y)1026 gdk_win32_surface_do_move (GdkSurface *window,
1027                            int x, int y)
1028 {
1029   RECT outer_rect;
1030   GdkWin32Surface *impl;
1031 
1032   g_return_if_fail (GDK_IS_SURFACE (window));
1033 
1034   if (GDK_SURFACE_DESTROYED (window))
1035     return;
1036 
1037   GDK_NOTE (MISC, g_print ("gdk_win32_surface_move: %p: %+d%+d\n",
1038                            GDK_SURFACE_HWND (window), x, y));
1039 
1040   if (window->state & GDK_TOPLEVEL_STATE_FULLSCREEN)
1041     return;
1042 
1043   impl = GDK_WIN32_SURFACE (window);
1044   get_outer_rect (window, window->width, window->height, &outer_rect);
1045 
1046   GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,%d,%d,0,0,"
1047                            "NOACTIVATE|NOSIZE|NOZORDER)\n",
1048                            GDK_SURFACE_HWND (window),
1049                            (x - _gdk_offset_x) * impl->surface_scale,
1050                            (y - _gdk_offset_y) * impl->surface_scale));
1051 
1052   API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window),
1053                            SWP_NOZORDER_SPECIFIED,
1054                            (x - _gdk_offset_x) * impl->surface_scale,
1055                            (y - _gdk_offset_y) * impl->surface_scale,
1056                            0, 0,
1057                            SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER));
1058 }
1059 
1060 void
gdk_win32_surface_resize(GdkSurface * window,int width,int height)1061 gdk_win32_surface_resize (GdkSurface *window,
1062 			 int width, int height)
1063 {
1064   RECT outer_rect;
1065 
1066   g_return_if_fail (GDK_IS_SURFACE (window));
1067 
1068   if (GDK_SURFACE_DESTROYED (window))
1069     return;
1070 
1071   if (width < 1)
1072     width = 1;
1073   if (height < 1)
1074     height = 1;
1075 
1076   GDK_NOTE (MISC, g_print ("gdk_win32_surface_resize: %p: %dx%d\n",
1077                            GDK_SURFACE_HWND (window), width, height));
1078 
1079   if (window->state & GDK_TOPLEVEL_STATE_FULLSCREEN)
1080     return;
1081 
1082   get_outer_rect (window, width, height, &outer_rect);
1083 
1084   GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,0,0,%ld,%ld,"
1085                            "NOACTIVATE|NOMOVE|NOZORDER)\n",
1086                            GDK_SURFACE_HWND (window),
1087                            outer_rect.right - outer_rect.left,
1088                            outer_rect.bottom - outer_rect.top));
1089 
1090   API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window),
1091                            SWP_NOZORDER_SPECIFIED,
1092                            0, 0,
1093                            outer_rect.right - outer_rect.left,
1094                            outer_rect.bottom - outer_rect.top,
1095                            SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER));
1096   window->resize_count += 1;
1097 
1098   gdk_surface_request_layout (window);
1099 }
1100 
1101 static void
gdk_win32_surface_do_move_resize(GdkSurface * window,int x,int y,int width,int height)1102 gdk_win32_surface_do_move_resize (GdkSurface *window,
1103                                   int         x,
1104                                   int         y,
1105                                   int         width,
1106                                   int         height)
1107 {
1108   RECT outer_rect;
1109   GdkWin32Surface *impl;
1110 
1111   g_return_if_fail (GDK_IS_SURFACE (window));
1112 
1113   if (GDK_SURFACE_DESTROYED (window))
1114     return;
1115 
1116   if (width < 1)
1117     width = 1;
1118   if (height < 1)
1119     height = 1;
1120 
1121   if (window->state & GDK_TOPLEVEL_STATE_FULLSCREEN)
1122     return;
1123 
1124   GDK_NOTE (MISC, g_print ("gdk_win32_surface_move_resize: %p: %dx%d@%+d%+d\n",
1125                            GDK_SURFACE_HWND (window),
1126                            width, height, x, y));
1127 
1128   impl = GDK_WIN32_SURFACE (window);
1129 
1130   get_outer_rect (window, width, height, &outer_rect);
1131 
1132   GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,%d,%d,%ld,%ld,"
1133                            "NOACTIVATE|NOZORDER)\n",
1134                            GDK_SURFACE_HWND (window),
1135                            (x - _gdk_offset_x) * impl->surface_scale,
1136                            (y - _gdk_offset_y) * impl->surface_scale,
1137                            outer_rect.right - outer_rect.left,
1138                            outer_rect.bottom - outer_rect.top));
1139 
1140   API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window),
1141                            SWP_NOZORDER_SPECIFIED,
1142                            (x - _gdk_offset_x) * impl->surface_scale,
1143                            (y - _gdk_offset_y) * impl->surface_scale,
1144                            outer_rect.right - outer_rect.left,
1145                            outer_rect.bottom - outer_rect.top,
1146                            SWP_NOACTIVATE | SWP_NOZORDER));
1147 }
1148 
1149 static void
gdk_win32_surface_move_resize_internal(GdkSurface * window,gboolean with_move,int x,int y,int width,int height)1150 gdk_win32_surface_move_resize_internal (GdkSurface *window,
1151                                         gboolean    with_move,
1152                                         int         x,
1153                                         int         y,
1154                                         int         width,
1155                                         int         height)
1156 {
1157   GdkWin32Surface *surface = GDK_WIN32_SURFACE (window);
1158 
1159   /* We ignore changes to the window being moved or resized by the
1160      user, as we don't want to fight the user */
1161   if (GDK_SURFACE_HWND (window) == _modal_move_resize_window)
1162     goto out;
1163 
1164   if (with_move && (width < 0 && height < 0))
1165     {
1166       gdk_win32_surface_do_move (window, x, y);
1167     }
1168   else
1169     {
1170       _gdk_win32_surface_invalidate_egl_framebuffer (window);
1171 
1172       if (with_move)
1173         {
1174           gdk_win32_surface_do_move_resize (window, x, y, width, height);
1175         }
1176       else
1177         {
1178           gdk_win32_surface_resize (window, width, height);
1179         }
1180     }
1181 
1182  out:
1183   gdk_surface_request_layout (window);
1184 }
1185 
1186 void
gdk_win32_surface_move_resize(GdkSurface * window,int x,int y,int width,int height)1187 gdk_win32_surface_move_resize (GdkSurface *window,
1188                                int         x,
1189                                int         y,
1190                                int         width,
1191                                int         height)
1192 {
1193   gdk_win32_surface_move_resize_internal (window, TRUE, x, y, width, height);
1194 }
1195 
1196 void
gdk_win32_surface_move(GdkSurface * surface,int x,int y)1197 gdk_win32_surface_move (GdkSurface *surface,
1198                         int         x,
1199                         int         y)
1200 {
1201   gdk_win32_surface_move_resize_internal (surface, TRUE, x, y, -1, -1);
1202 }
1203 
1204 static void gdk_win32_surface_set_shadow_width (GdkSurface *window,
1205                                                 int        left,
1206                                                 int        right,
1207                                                 int        top,
1208                                                 int        bottom);
1209 
1210 static void
gdk_win32_surface_layout_popup(GdkSurface * surface,int width,int height,GdkPopupLayout * layout)1211 gdk_win32_surface_layout_popup (GdkSurface     *surface,
1212                                 int             width,
1213                                 int             height,
1214                                 GdkPopupLayout *layout)
1215 {
1216   GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
1217   GdkMonitor *monitor;
1218   GdkRectangle bounds;
1219   GdkRectangle final_rect;
1220   int x, y;
1221   int shadow_left, shadow_right, shadow_top, shadow_bottom;
1222 
1223   monitor = gdk_surface_get_layout_monitor (surface, layout,
1224                                             gdk_win32_monitor_get_workarea);
1225   gdk_win32_monitor_get_workarea (monitor, &bounds);
1226 
1227   gdk_popup_layout_get_shadow_width (layout,
1228                                      &shadow_left,
1229                                      &shadow_right,
1230                                      &shadow_top,
1231                                      &shadow_bottom);
1232 
1233   gdk_win32_surface_set_shadow_width (surface,
1234                                       shadow_left,
1235                                       shadow_right,
1236                                       shadow_top,
1237                                       shadow_bottom);
1238 
1239   gdk_surface_layout_popup_helper (surface,
1240                                    width,
1241                                    height,
1242                                    shadow_left,
1243                                    shadow_right,
1244                                    shadow_top,
1245                                    shadow_bottom,
1246                                    monitor,
1247                                    &bounds,
1248                                    layout,
1249                                    &final_rect);
1250 
1251   gdk_surface_get_origin (surface->parent, &x, &y);
1252   x += final_rect.x;
1253   y += final_rect.y;
1254 
1255   if (final_rect.width != surface->width ||
1256       final_rect.height != surface->height)
1257     {
1258       gdk_win32_surface_move_resize (surface,
1259                                      x,
1260                                      y,
1261                                      final_rect.width,
1262                                      final_rect.height);
1263     }
1264   else
1265     gdk_win32_surface_move (surface, x, y);
1266 }
1267 
1268 static void
maybe_notify_mapped(GdkSurface * surface)1269 maybe_notify_mapped (GdkSurface *surface)
1270 {
1271   if (surface->destroyed)
1272     return;
1273 
1274   if (!GDK_SURFACE_IS_MAPPED (surface))
1275     {
1276       gdk_surface_set_is_mapped (surface, TRUE);
1277       gdk_surface_invalidate_rect (surface, NULL);
1278     }
1279 }
1280 
1281 static void
show_popup(GdkSurface * surface)1282 show_popup (GdkSurface *surface)
1283 {
1284   gdk_win32_surface_raise (surface);
1285   maybe_notify_mapped (surface);
1286   show_window_internal (surface, FALSE, FALSE);
1287   gdk_surface_invalidate_rect (surface, NULL);
1288 }
1289 
1290 static void
show_grabbing_popup(GdkSeat * seat,GdkSurface * surface,gpointer user_data)1291 show_grabbing_popup (GdkSeat    *seat,
1292                      GdkSurface *surface,
1293                      gpointer    user_data)
1294 {
1295   show_popup (surface);
1296 }
1297 
1298 static gboolean
gdk_win32_surface_present_popup(GdkSurface * surface,int width,int height,GdkPopupLayout * layout)1299 gdk_win32_surface_present_popup (GdkSurface     *surface,
1300                                  int             width,
1301                                  int             height,
1302                                  GdkPopupLayout *layout)
1303 {
1304   gdk_win32_surface_layout_popup (surface, width, height, layout);
1305 
1306   if (GDK_SURFACE_IS_MAPPED (surface))
1307     return TRUE;
1308 
1309   if (surface->autohide)
1310     {
1311       gdk_seat_grab (gdk_display_get_default_seat (surface->display),
1312                      surface,
1313                      GDK_SEAT_CAPABILITY_ALL,
1314                      TRUE,
1315                      NULL, NULL,
1316                      show_grabbing_popup, NULL);
1317     }
1318   else
1319     {
1320       show_popup (surface);
1321     }
1322 
1323   return GDK_SURFACE_IS_MAPPED (surface);
1324 }
1325 
1326 void
gdk_win32_surface_raise(GdkSurface * window)1327 gdk_win32_surface_raise (GdkSurface *window)
1328 {
1329   if (!GDK_SURFACE_DESTROYED (window))
1330     {
1331       GDK_NOTE (MISC, g_print ("gdk_win32_surface_raise: %p\n",
1332 			       GDK_SURFACE_HWND (window)));
1333 
1334       if (GDK_IS_DRAG_SURFACE (window))
1335         API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_TOPMOST,
1336 	                         0, 0, 0, 0,
1337 				 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER));
1338 
1339       else if (GDK_IS_POPUP (window))
1340         ShowWindow (GDK_SURFACE_HWND (window), SW_SHOWNOACTIVATE);
1341       else
1342         /* Do not wrap this in an API_CALL macro as SetForegroundWindow might
1343          * fail when for example dragging a window belonging to a different
1344          * application at the time of a gtk_window_present() call due to focus
1345          * stealing prevention. */
1346         SetForegroundWindow (GDK_SURFACE_HWND (window));
1347     }
1348 }
1349 
1350 void
gdk_win32_surface_set_urgency_hint(GdkSurface * window,gboolean urgent)1351 gdk_win32_surface_set_urgency_hint (GdkSurface *window,
1352                                     gboolean    urgent)
1353 {
1354   FLASHWINFO flashwinfo;
1355   typedef BOOL (WINAPI *PFN_FlashWindowEx) (FLASHWINFO*);
1356   PFN_FlashWindowEx flashWindowEx = NULL;
1357 
1358   g_return_if_fail (GDK_IS_SURFACE (window));
1359 
1360   if (GDK_SURFACE_DESTROYED (window))
1361     return;
1362 
1363   flashWindowEx = (PFN_FlashWindowEx) GetProcAddress (GetModuleHandle ("user32.dll"), "FlashWindowEx");
1364 
1365   if (flashWindowEx)
1366     {
1367       flashwinfo.cbSize = sizeof (flashwinfo);
1368       flashwinfo.hwnd = GDK_SURFACE_HWND (window);
1369       if (urgent)
1370 	flashwinfo.dwFlags = FLASHW_ALL | FLASHW_TIMER;
1371       else
1372 	flashwinfo.dwFlags = FLASHW_STOP;
1373       flashwinfo.uCount = 0;
1374       flashwinfo.dwTimeout = 0;
1375 
1376       flashWindowEx (&flashwinfo);
1377     }
1378   else
1379     {
1380       FlashWindow (GDK_SURFACE_HWND (window), urgent);
1381     }
1382 }
1383 
1384 static gboolean
get_effective_window_decorations(GdkSurface * window,GdkWMDecoration * decoration)1385 get_effective_window_decorations (GdkSurface       *window,
1386                                   GdkWMDecoration *decoration)
1387 {
1388   GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
1389 
1390   *decoration = 0;
1391 
1392   if (!GDK_IS_TOPLEVEL (window))
1393     return FALSE;
1394 
1395   /* we want to apply the "no decorations", if decorations are disabled */
1396   if (!GDK_WIN32_SURFACE (window)->decorate_all)
1397     return TRUE;
1398 
1399   if ((impl->hint_flags & GDK_HINT_MIN_SIZE) &&
1400       (impl->hint_flags & GDK_HINT_MAX_SIZE) &&
1401       impl->hints.min_width == impl->hints.max_width &&
1402       impl->hints.min_height == impl->hints.max_height)
1403     {
1404       *decoration = GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MAXIMIZE;
1405 
1406       *decoration |= GDK_DECOR_MINIMIZE;
1407 
1408       return TRUE;
1409     }
1410   else if (impl->hint_flags & GDK_HINT_MAX_SIZE)
1411     {
1412       *decoration = GDK_DECOR_ALL | GDK_DECOR_MAXIMIZE;
1413       *decoration |= GDK_DECOR_MINIMIZE;
1414 
1415       return TRUE;
1416     }
1417   else
1418     {
1419       *decoration = (GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE);
1420       return TRUE;
1421     }
1422 
1423   return FALSE;
1424 }
1425 
1426 static void
gdk_win32_surface_set_geometry_hints(GdkSurface * window,const GdkGeometry * geometry,GdkSurfaceHints geom_mask)1427 gdk_win32_surface_set_geometry_hints (GdkSurface         *window,
1428 			       const GdkGeometry *geometry,
1429 			       GdkSurfaceHints     geom_mask)
1430 {
1431   GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
1432   FullscreenInfo *fi;
1433 
1434   g_return_if_fail (GDK_IS_SURFACE (window));
1435 
1436   if (GDK_SURFACE_DESTROYED (window))
1437     return;
1438 
1439   GDK_NOTE (MISC, g_print ("gdk_surface_set_geometry_hints: %p\n",
1440 			   GDK_SURFACE_HWND (window)));
1441 
1442   fi = g_object_get_data (G_OBJECT (window), "fullscreen-info");
1443   if (fi)
1444     fi->hint_flags = geom_mask;
1445   else
1446     impl->hint_flags = geom_mask;
1447   impl->hints = *geometry;
1448 
1449   if (geom_mask & GDK_HINT_MIN_SIZE)
1450     {
1451       GDK_NOTE (MISC, g_print ("... MIN_SIZE: %dx%d\n",
1452 			       geometry->min_width, geometry->min_height));
1453     }
1454 
1455   if (geom_mask & GDK_HINT_MAX_SIZE)
1456     {
1457       GDK_NOTE (MISC, g_print ("... MAX_SIZE: %dx%d\n",
1458 			       geometry->max_width, geometry->max_height));
1459     }
1460 
1461   _gdk_win32_surface_update_style_bits (window);
1462 }
1463 
1464 static void
gdk_win32_surface_set_title(GdkSurface * window,const char * title)1465 gdk_win32_surface_set_title (GdkSurface   *window,
1466 		      const char *title)
1467 {
1468   wchar_t *wtitle;
1469 
1470   g_return_if_fail (GDK_IS_SURFACE (window));
1471   g_return_if_fail (title != NULL);
1472 
1473   if (GDK_SURFACE_DESTROYED (window))
1474     return;
1475 
1476   /* Empty window titles not allowed, so set it to just a period. */
1477   if (!title[0])
1478     title = ".";
1479 
1480   GDK_NOTE (MISC, g_print ("gdk_surface_set_title: %p: %s\n",
1481 			   GDK_SURFACE_HWND (window), title));
1482 
1483   GDK_NOTE (MISC_OR_EVENTS, title = g_strdup_printf ("%p %s", GDK_SURFACE_HWND (window), title));
1484 
1485   wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL);
1486   API_CALL (SetWindowTextW, (GDK_SURFACE_HWND (window), wtitle));
1487   g_free (wtitle);
1488 
1489   GDK_NOTE (MISC_OR_EVENTS, g_free ((char *) title));
1490 }
1491 
1492 static void
gdk_win32_surface_set_transient_for(GdkSurface * window,GdkSurface * parent)1493 gdk_win32_surface_set_transient_for (GdkSurface *window,
1494 			      GdkSurface *parent)
1495 {
1496   HWND window_id, parent_id;
1497   LONG_PTR old_ptr;
1498   DWORD w32_error;
1499   GdkWin32Surface *surface = GDK_WIN32_SURFACE (window);
1500   GdkWin32Surface *parent_impl = NULL;
1501   GSList *item;
1502 
1503   g_return_if_fail (GDK_IS_SURFACE (window));
1504 
1505   window_id = GDK_SURFACE_HWND (window);
1506   parent_id = parent != NULL ? GDK_SURFACE_HWND (parent) : NULL;
1507 
1508   GDK_NOTE (MISC, g_print ("gdk_surface_set_transient_for: %p: %p\n", window_id, parent_id));
1509 
1510   if (GDK_SURFACE_DESTROYED (window) || (parent && GDK_SURFACE_DESTROYED (parent)))
1511     {
1512       if (GDK_SURFACE_DESTROYED (window))
1513 	GDK_NOTE (MISC, g_print ("... destroyed!\n"));
1514       else
1515 	GDK_NOTE (MISC, g_print ("... owner destroyed!\n"));
1516 
1517       return;
1518     }
1519 
1520   if (surface->transient_owner == parent)
1521     return;
1522 
1523   if (GDK_IS_SURFACE (surface->transient_owner))
1524     {
1525       GdkWin32Surface *trans_impl = GDK_WIN32_SURFACE (surface->transient_owner);
1526       item = g_slist_find (trans_impl->transient_children, window);
1527       item->data = NULL;
1528       trans_impl->transient_children = g_slist_delete_link (trans_impl->transient_children, item);
1529       trans_impl->num_transients--;
1530 
1531       if (!trans_impl->num_transients)
1532         {
1533           trans_impl->transient_children = NULL;
1534         }
1535 
1536       g_object_unref (G_OBJECT (surface->transient_owner));
1537       g_object_unref (G_OBJECT (window));
1538 
1539       surface->transient_owner = NULL;
1540     }
1541 
1542   if (parent)
1543     {
1544       parent_impl = GDK_WIN32_SURFACE (parent);
1545 
1546       parent_impl->transient_children = g_slist_append (parent_impl->transient_children, window);
1547       g_object_ref (G_OBJECT (window));
1548       parent_impl->num_transients++;
1549       surface->transient_owner = parent;
1550       g_object_ref (G_OBJECT (parent));
1551     }
1552 
1553   SetLastError (0);
1554   old_ptr = GetWindowLongPtr (window_id, GWLP_HWNDPARENT);
1555   w32_error = GetLastError ();
1556 
1557   /* Don't re-set GWLP_HWNDPARENT to the same value */
1558   if ((HWND) old_ptr == parent_id && w32_error == NO_ERROR)
1559     return;
1560 
1561   /* Don't return if it failed, try SetWindowLongPtr() anyway */
1562   if (old_ptr == 0 && w32_error != NO_ERROR)
1563     WIN32_API_FAILED ("GetWindowLongPtr");
1564 
1565   /* This changes the *owner* of the window, despite the misleading
1566    * name. (Owner and parent are unrelated concepts.) At least that's
1567    * what people who seem to know what they talk about say on
1568    * USENET. Search on Google.
1569    */
1570   SetLastError (0);
1571   old_ptr = SetWindowLongPtr (window_id, GWLP_HWNDPARENT, (LONG_PTR) parent_id);
1572   w32_error = GetLastError ();
1573 
1574   if (old_ptr == 0 && w32_error != NO_ERROR)
1575     WIN32_API_FAILED ("SetWindowLongPtr");
1576 }
1577 
1578 void
_gdk_push_modal_window(GdkSurface * window)1579 _gdk_push_modal_window (GdkSurface *window)
1580 {
1581   modal_window_stack = g_slist_prepend (modal_window_stack, window);
1582 }
1583 
1584 void
_gdk_remove_modal_window(GdkSurface * window)1585 _gdk_remove_modal_window (GdkSurface *window)
1586 {
1587   GSList *tmp;
1588 
1589   g_return_if_fail (window != NULL);
1590 
1591   /* It's possible to be NULL here if someone sets the modal hint of the window
1592    * to FALSE before a modal window stack has ever been created. */
1593   if (modal_window_stack == NULL)
1594     return;
1595 
1596   /* Find the requested window in the stack and remove it.  Yeah, I realize this
1597    * means we're not a 'real stack', strictly speaking.  Sue me. :) */
1598   tmp = g_slist_find (modal_window_stack, window);
1599   if (tmp != NULL)
1600     {
1601       modal_window_stack = g_slist_delete_link (modal_window_stack, tmp);
1602     }
1603 }
1604 
1605 gboolean
_gdk_modal_blocked(GdkSurface * window)1606 _gdk_modal_blocked (GdkSurface *window)
1607 {
1608   GSList *l;
1609   gboolean found_any = FALSE;
1610 
1611   for (l = modal_window_stack; l != NULL; l = l->next)
1612     {
1613       GdkSurface *modal = l->data;
1614 
1615       if (modal == window)
1616 	return FALSE;
1617 
1618       if (GDK_SURFACE_IS_MAPPED (modal))
1619 	found_any = TRUE;
1620     }
1621 
1622   return found_any;
1623 }
1624 
1625 GdkSurface *
_gdk_modal_current(void)1626 _gdk_modal_current (void)
1627 {
1628   GSList *l;
1629 
1630   for (l = modal_window_stack; l != NULL; l = l->next)
1631     {
1632       GdkSurface *modal = l->data;
1633 
1634       if (GDK_SURFACE_IS_MAPPED (modal))
1635 	return modal;
1636     }
1637 
1638   return NULL;
1639 }
1640 
1641 static void
gdk_win32_surface_get_geometry(GdkSurface * window,int * x,int * y,int * width,int * height)1642 gdk_win32_surface_get_geometry (GdkSurface *window,
1643 			       int       *x,
1644 			       int       *y,
1645 			       int       *width,
1646 			       int       *height)
1647 {
1648   if (!GDK_SURFACE_DESTROYED (window))
1649     {
1650       RECT rect;
1651       GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
1652 
1653       if (GDK_IS_TOPLEVEL (window) && impl->drag_move_resize_context.native_move_resize_pending)
1654          rect = impl->next_layout.configured_rect;
1655       else
1656         {
1657           POINT pt;
1658           GdkSurface *parent;
1659 
1660           if (GDK_IS_TOPLEVEL (window))
1661             parent = NULL;
1662           else if (GDK_IS_POPUP (window))
1663             parent = gdk_popup_get_parent (GDK_POPUP (window));
1664           else
1665             parent = NULL;
1666 
1667           API_CALL (GetClientRect, (GDK_SURFACE_HWND (window), &rect));
1668 
1669           pt.x = rect.left;
1670           pt.y = rect.top;
1671 	      ClientToScreen (GDK_SURFACE_HWND (window), &pt);
1672           if (parent)
1673             ScreenToClient (GDK_SURFACE_HWND (parent), &pt);
1674 
1675           rect.left = pt.x;
1676           rect.top = pt.y;
1677 
1678           pt.x = rect.right;
1679           pt.y = rect.bottom;
1680           ClientToScreen (GDK_SURFACE_HWND (window), &pt);
1681           if (parent)
1682             ScreenToClient (GDK_SURFACE_HWND (parent), &pt);
1683 
1684           rect.right = pt.x;
1685           rect.bottom = pt.y;
1686 
1687 	      if (parent == NULL)
1688 	        {
1689 	          rect.left += _gdk_offset_x * impl->surface_scale;
1690 	          rect.top += _gdk_offset_y * impl->surface_scale;
1691 	          rect.right += _gdk_offset_x * impl->surface_scale;
1692 	          rect.bottom += _gdk_offset_y * impl->surface_scale;
1693 	        }
1694         }
1695 
1696       if (x)
1697 	*x = rect.left / impl->surface_scale;
1698       if (y)
1699 	*y = rect.top / impl->surface_scale;
1700       if (width)
1701 	*width = (rect.right - rect.left) / impl->surface_scale;
1702       if (height)
1703 	*height = (rect.bottom - rect.top) / impl->surface_scale;
1704 
1705       GDK_NOTE (MISC, g_print ("gdk_win32_surface_get_geometry: %p: %ldx%ld@%+ld%+ld\n",
1706 			       GDK_SURFACE_HWND (window),
1707 			       (rect.right - rect.left) / impl->surface_scale,
1708 			       (rect.bottom - rect.top) / impl->surface_scale,
1709 			       rect.left, rect.top));
1710     }
1711 }
1712 
1713 static void
gdk_win32_surface_get_root_coords(GdkSurface * window,int x,int y,int * root_x,int * root_y)1714 gdk_win32_surface_get_root_coords (GdkSurface *window,
1715 				  int        x,
1716 				  int        y,
1717 				  int       *root_x,
1718 				  int       *root_y)
1719 {
1720   int tx;
1721   int ty;
1722   POINT pt;
1723   GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
1724 
1725   pt.x = x * impl->surface_scale;
1726   pt.y = y * impl->surface_scale;
1727   ClientToScreen (GDK_SURFACE_HWND (window), &pt);
1728   tx = pt.x;
1729   ty = pt.y;
1730 
1731   if (root_x)
1732     *root_x = (tx + _gdk_offset_x) / impl->surface_scale;
1733   if (root_y)
1734     *root_y = (ty + _gdk_offset_y) / impl->surface_scale;
1735 
1736   GDK_NOTE (MISC, g_print ("gdk_win32_surface_get_root_coords: %p: %+d%+d %+d%+d\n",
1737 			   GDK_SURFACE_HWND (window),
1738 			   x * impl->surface_scale,
1739 			   y * impl->surface_scale,
1740 			   (tx + _gdk_offset_x) / impl->surface_scale,
1741 			   (ty + _gdk_offset_y) / impl->surface_scale));
1742 }
1743 
1744 static gboolean
gdk_surface_win32_get_device_state(GdkSurface * window,GdkDevice * device,double * x,double * y,GdkModifierType * mask)1745 gdk_surface_win32_get_device_state (GdkSurface     *window,
1746                                     GdkDevice       *device,
1747                                     double          *x,
1748                                     double          *y,
1749                                     GdkModifierType *mask)
1750 {
1751   _gdk_device_win32_query_state (device, window, NULL, x, y, mask);
1752 
1753   return *x >= 0 && *y >= 0 && *x < window->width && *y < window->height;
1754 
1755 }
1756 
1757 static void
update_single_bit(LONG * style,gboolean all,int gdk_bit,int style_bit)1758 update_single_bit (LONG    *style,
1759                    gboolean all,
1760 		   int      gdk_bit,
1761 		   int      style_bit)
1762 {
1763   /* all controls the interpretation of gdk_bit -- if all is TRUE,
1764    * gdk_bit indicates whether style_bit is off; if all is FALSE, gdk
1765    * bit indicate whether style_bit is on
1766    */
1767   if ((!all && gdk_bit) || (all && !gdk_bit))
1768     *style |= style_bit;
1769   else
1770     *style &= ~style_bit;
1771 }
1772 
1773 /*
1774  * Returns TRUE if window has no decorations.
1775  * Usually it means CSD windows, because GTK
1776  * calls gdk_surface_set_decorations (window, 0);
1777  */
1778 gboolean
_gdk_win32_surface_lacks_wm_decorations(GdkSurface * window)1779 _gdk_win32_surface_lacks_wm_decorations (GdkSurface *window)
1780 {
1781   GdkWin32Surface *impl;
1782   LONG style;
1783   gboolean has_any_decorations;
1784 
1785   if (GDK_SURFACE_DESTROYED (window))
1786     return FALSE;
1787 
1788   impl = GDK_WIN32_SURFACE (window);
1789 
1790   /* This is because GTK calls gdk_surface_set_decorations (window, 0),
1791    * even though GdkWMDecoration docs indicate that 0 does NOT mean
1792    * "no decorations".
1793    */
1794   if (!impl->decorate_all)
1795     return TRUE;
1796 
1797   if (GDK_SURFACE_HWND (window) == 0)
1798     return FALSE;
1799 
1800   style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE);
1801 
1802   if (style == 0)
1803     {
1804       DWORD w32_error = GetLastError ();
1805 
1806       GDK_NOTE (MISC, g_print ("Failed to get style of window %p (handle %p): %lu\n",
1807                                window, GDK_SURFACE_HWND (window), w32_error));
1808       return FALSE;
1809     }
1810 
1811   /* Keep this in sync with _gdk_win32_surface_update_style_bits() */
1812   /* We don't check what get_effective_window_decorations()
1813    * has to say, because it gives suggestions based on
1814    * various hints, while we want *actual* decorations,
1815    * or their absence.
1816    */
1817   has_any_decorations = FALSE;
1818 
1819   if (style & (WS_BORDER | WS_THICKFRAME | WS_CAPTION |
1820                WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX))
1821     has_any_decorations = TRUE;
1822   else
1823     GDK_NOTE (MISC, g_print ("Window %p (handle %p): has no decorations (style %lx)\n",
1824                              window, GDK_SURFACE_HWND (window), style));
1825 
1826   return !has_any_decorations;
1827 }
1828 
1829 void
_gdk_win32_surface_update_style_bits(GdkSurface * window)1830 _gdk_win32_surface_update_style_bits (GdkSurface *window)
1831 {
1832   GdkWin32Surface *impl = (GdkWin32Surface *)window;
1833   GdkWMDecoration decorations;
1834   LONG old_style, new_style, old_exstyle, new_exstyle;
1835   gboolean all;
1836   RECT rect, before, after;
1837   gboolean was_topmost;
1838   gboolean will_be_topmost;
1839   gboolean was_layered;
1840   gboolean will_be_layered;
1841   HWND insert_after;
1842   UINT flags;
1843 
1844   if (window->state & GDK_TOPLEVEL_STATE_FULLSCREEN)
1845     return;
1846 
1847   old_style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE);
1848   old_exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE);
1849 
1850   GetClientRect (GDK_SURFACE_HWND (window), &before);
1851   after = before;
1852   AdjustWindowRectEx (&before, old_style, FALSE, old_exstyle);
1853 
1854   was_topmost = (old_exstyle & WS_EX_TOPMOST) ? TRUE : FALSE;
1855   was_layered = (old_exstyle & WS_EX_LAYERED) ? TRUE : FALSE;
1856   will_be_topmost = was_topmost;
1857   will_be_layered = was_layered;
1858 
1859   old_exstyle &= ~WS_EX_TOPMOST;
1860 
1861   new_style = old_style;
1862   new_exstyle = old_exstyle;
1863 
1864   if (GDK_IS_DRAG_SURFACE (window))
1865     {
1866       new_exstyle |= WS_EX_TOOLWINDOW;
1867 
1868       /* WS_EX_LAYERED | WS_EX_TRANSPARENT makes the drag surface behave
1869        * in pointer input passthrough mode, so it doesn't interfere with
1870        * the drag and drop operation.
1871        */
1872       new_exstyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT;
1873       will_be_topmost = TRUE;
1874       will_be_layered = TRUE;
1875     }
1876   else
1877     {
1878       new_exstyle &= ~WS_EX_TOOLWINDOW;
1879     }
1880 
1881   if (get_effective_window_decorations (window, &decorations))
1882     {
1883       all = (decorations & GDK_DECOR_ALL);
1884 
1885       /* Keep this in sync with the test in _gdk_win32_surface_lacks_wm_decorations() */
1886       update_single_bit (&new_style, all, decorations & GDK_DECOR_BORDER, WS_BORDER);
1887       update_single_bit (&new_style, all, decorations & GDK_DECOR_RESIZEH, WS_THICKFRAME);
1888       update_single_bit (&new_style, all, decorations & GDK_DECOR_TITLE, WS_CAPTION);
1889       update_single_bit (&new_style, all, decorations & GDK_DECOR_MENU, WS_SYSMENU);
1890       update_single_bit (&new_style, all, decorations & GDK_DECOR_MINIMIZE, WS_MINIMIZEBOX);
1891       update_single_bit (&new_style, all, decorations & GDK_DECOR_MAXIMIZE, WS_MAXIMIZEBOX);
1892     }
1893 
1894   if (old_style == new_style && old_exstyle == new_exstyle )
1895     {
1896       GDK_NOTE (MISC, g_print ("_gdk_win32_surface_update_style_bits: %p: no change\n",
1897 			       GDK_SURFACE_HWND (window)));
1898       return;
1899     }
1900 
1901   if (old_style != new_style)
1902     {
1903       GDK_NOTE (MISC, g_print ("_gdk_win32_surface_update_style_bits: %p: STYLE: %s => %s\n",
1904 			       GDK_SURFACE_HWND (window),
1905 			       _gdk_win32_surface_style_to_string (old_style),
1906 			       _gdk_win32_surface_style_to_string (new_style)));
1907 
1908       SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, new_style);
1909     }
1910 
1911   if (old_exstyle != new_exstyle)
1912     {
1913       GDK_NOTE (MISC, g_print ("_gdk_win32_surface_update_style_bits: %p: EXSTYLE: %s => %s\n",
1914 			       GDK_SURFACE_HWND (window),
1915 			       _gdk_win32_surface_exstyle_to_string (old_exstyle),
1916 			       _gdk_win32_surface_exstyle_to_string (new_exstyle)));
1917 
1918       SetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE, new_exstyle);
1919 
1920       if (!was_layered && will_be_layered)
1921         {
1922           /* We have to call SetLayeredWindowAttributes when setting the
1923            * WS_EX_LAYERED style anew, otherwise the window won't show up
1924            */
1925           API_CALL (SetLayeredWindowAttributes, (GDK_SURFACE_HWND (window), 0, 255, LWA_ALPHA));
1926         }
1927     }
1928 
1929   AdjustWindowRectEx (&after, new_style, FALSE, new_exstyle);
1930 
1931   GetWindowRect (GDK_SURFACE_HWND (window), &rect);
1932   rect.left += after.left - before.left;
1933   rect.top += after.top - before.top;
1934   rect.right += after.right - before.right;
1935   rect.bottom += after.bottom - before.bottom;
1936 
1937   flags = SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOREPOSITION;
1938 
1939   if (will_be_topmost && !was_topmost)
1940     {
1941       insert_after = HWND_TOPMOST;
1942     }
1943   else if (was_topmost && !will_be_topmost)
1944     {
1945       insert_after = HWND_NOTOPMOST;
1946     }
1947   else
1948     {
1949       flags |= SWP_NOZORDER;
1950       insert_after = SWP_NOZORDER_SPECIFIED;
1951     }
1952 
1953   SetWindowPos (GDK_SURFACE_HWND (window), insert_after,
1954 		rect.left, rect.top,
1955 		rect.right - rect.left, rect.bottom - rect.top,
1956 		flags);
1957 }
1958 
1959 #if defined(MORE_AEROSNAP_DEBUGGING)
1960 static void
log_region(char * prefix,AeroSnapEdgeRegion * region)1961 log_region (char *prefix, AeroSnapEdgeRegion *region)
1962 {
1963   GDK_NOTE (MISC, g_print ("Region %s:\n"
1964                            "edge %d x %d @ %d x %d\n"
1965                            "trig %d x %d @ %d x %d\n",
1966                            prefix,
1967                            region->edge.width,
1968                            region->edge.height,
1969                            region->edge.x,
1970                            region->edge.y,
1971                            region->trigger.width,
1972                            region->trigger.height,
1973                            region->trigger.x,
1974                            region->trigger.y));
1975 }
1976 #endif
1977 
1978 static void
calculate_aerosnap_regions(GdkW32DragMoveResizeContext * context)1979 calculate_aerosnap_regions (GdkW32DragMoveResizeContext *context)
1980 {
1981   GdkDisplay *display;
1982   GListModel *monitors;
1983   int monitor_idx, other_monitor_idx;
1984   GdkWin32Surface *impl = GDK_WIN32_SURFACE (context->window);
1985 #if defined(MORE_AEROSNAP_DEBUGGING)
1986   int i;
1987 #endif
1988 
1989   display = gdk_surface_get_display (context->window);
1990   monitors = gdk_display_get_monitors (display);
1991 
1992 #define _M_UP 0
1993 #define _M_DOWN 1
1994 #define _M_LEFT 2
1995 #define _M_RIGHT 3
1996 
1997   for (monitor_idx = 0; monitor_idx < g_list_model_get_n_items (monitors); monitor_idx++)
1998     {
1999       GdkRectangle wa;
2000       GdkRectangle geometry;
2001       AeroSnapEdgeRegion snap_region;
2002       gboolean move_edge[4] = { TRUE, FALSE, TRUE, TRUE };
2003       gboolean resize_edge[2] = { TRUE, TRUE };
2004       int diff;
2005       int thickness, trigger_thickness;
2006       GdkMonitor *monitor;
2007 
2008       monitor = g_list_model_get_item (monitors, monitor_idx);
2009       g_object_unref (monitor);
2010       gdk_win32_monitor_get_workarea (monitor, &wa);
2011       gdk_monitor_get_geometry (monitor, &geometry);
2012 
2013       for (other_monitor_idx = 0;
2014            other_monitor_idx < g_list_model_get_n_items (monitors) &&
2015            (move_edge[_M_UP] || move_edge[_M_LEFT] ||
2016            move_edge[_M_RIGHT] || resize_edge[_M_DOWN]);
2017            other_monitor_idx++)
2018         {
2019           GdkRectangle other_wa;
2020           GdkMonitor *other_monitor;
2021 
2022           if (other_monitor_idx == monitor_idx)
2023             continue;
2024 
2025           other_monitor = g_list_model_get_item (monitors, other_monitor_idx);
2026           g_object_unref (other_monitor);
2027           gdk_win32_monitor_get_workarea (other_monitor, &other_wa);
2028 
2029           /* An edge triggers AeroSnap only if there are no
2030            * monitors beyond that edge.
2031            * Even if there's another monitor, but it does not cover
2032            * the whole edge (it's smaller or is not aligned to
2033            * the corner of current monitor), that edge is still
2034            * removed from the trigger list.
2035            */
2036           if (other_wa.x >= wa.x + wa.width)
2037             move_edge[_M_RIGHT] = FALSE;
2038 
2039           if (other_wa.x + other_wa.width <= wa.x)
2040             move_edge[_M_LEFT] = FALSE;
2041 
2042           if (other_wa.y + other_wa.height <= wa.y)
2043             {
2044               move_edge[_M_UP] = FALSE;
2045               resize_edge[_M_UP] = FALSE;
2046             }
2047 
2048           if (other_wa.y >= wa.y + wa.height)
2049             {
2050               /* no move_edge for the bottom edge, just resize_edge */
2051               resize_edge[_M_DOWN] = FALSE;
2052             }
2053         }
2054 
2055       thickness = AEROSNAP_REGION_THICKNESS * impl->surface_scale;
2056       trigger_thickness = AEROSNAP_REGION_TRIGGER_THICKNESS * impl->surface_scale;
2057 
2058       snap_region.edge = wa;
2059       snap_region.trigger = wa;
2060       snap_region.edge.height = thickness;
2061       snap_region.trigger.height = trigger_thickness;
2062 
2063       /* Extend both regions into toolbar space.
2064        * When there's no toolbar, diff == 0.
2065        */
2066       diff = wa.y - geometry.y;
2067       snap_region.edge.height += diff;
2068       snap_region.edge.y -= diff;
2069       snap_region.trigger.height += diff;
2070       snap_region.trigger.y -= diff;
2071 
2072       if (move_edge[_M_UP])
2073         g_array_append_val (context->maximize_regions, snap_region);
2074 
2075       if (resize_edge[_M_UP])
2076         g_array_append_val (context->fullup_regions, snap_region);
2077 
2078       snap_region.edge = wa;
2079       snap_region.trigger = wa;
2080       snap_region.edge.width = thickness;
2081       snap_region.trigger.width = trigger_thickness;
2082 
2083       diff = wa.x - geometry.x;
2084       snap_region.edge.width += diff;
2085       snap_region.edge.x -= diff;
2086       snap_region.trigger.width += diff;
2087       snap_region.trigger.x -= diff;
2088 
2089       if (move_edge[_M_LEFT])
2090         g_array_append_val (context->halfleft_regions, snap_region);
2091 
2092       snap_region.edge = wa;
2093       snap_region.trigger = wa;
2094       snap_region.edge.x += wa.width - thickness;
2095       snap_region.edge.width = thickness;
2096       snap_region.trigger.x += wa.width - trigger_thickness;
2097       snap_region.trigger.width = trigger_thickness;
2098 
2099       diff = (geometry.x + geometry.width) - (wa.x + wa.width);
2100       snap_region.edge.width += diff;
2101       snap_region.trigger.width += diff;
2102 
2103       if (move_edge[_M_RIGHT])
2104         g_array_append_val (context->halfright_regions, snap_region);
2105 
2106       snap_region.edge = wa;
2107       snap_region.trigger = wa;
2108       snap_region.edge.y += wa.height - thickness;
2109       snap_region.edge.height = thickness;
2110       snap_region.trigger.y += wa.height - trigger_thickness;
2111       snap_region.trigger.height = trigger_thickness;
2112 
2113       diff = (geometry.y + geometry.height) - (wa.y + wa.height);
2114       snap_region.edge.height += diff;
2115       snap_region.trigger.height += diff;
2116 
2117       if (resize_edge[_M_DOWN])
2118         g_array_append_val (context->fullup_regions, snap_region);
2119     }
2120 
2121 #undef _M_UP
2122 #undef _M_DOWN
2123 #undef _M_LEFT
2124 #undef _M_RIGHT
2125 
2126 #if defined(MORE_AEROSNAP_DEBUGGING)
2127   for (i = 0; i < context->maximize_regions->len; i++)
2128     log_region ("maximize", &g_array_index (context->maximize_regions, AeroSnapEdgeRegion, i));
2129 
2130   for (i = 0; i < context->halfleft_regions->len; i++)
2131     log_region ("halfleft", &g_array_index (context->halfleft_regions, AeroSnapEdgeRegion, i));
2132 
2133   for (i = 0; i < context->halfright_regions->len; i++)
2134     log_region ("halfright", &g_array_index (context->halfright_regions, AeroSnapEdgeRegion, i));
2135 
2136   for (i = 0; i < context->fullup_regions->len; i++)
2137     log_region ("fullup", &g_array_index (context->fullup_regions, AeroSnapEdgeRegion, i));
2138 #endif
2139 }
2140 
2141 static void
discard_snapinfo(GdkSurface * window)2142 discard_snapinfo (GdkSurface *window)
2143 {
2144   GdkWin32Surface *impl;
2145 
2146   impl = GDK_WIN32_SURFACE (window);
2147 
2148   impl->snap_state = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED;
2149 
2150   if (impl->snap_stash == NULL)
2151     return;
2152 
2153   g_clear_pointer (&impl->snap_stash, g_free);
2154   g_clear_pointer (&impl->snap_stash_int, g_free);
2155 }
2156 
2157 static void
unsnap(GdkSurface * window,GdkMonitor * monitor)2158 unsnap (GdkSurface  *window,
2159         GdkMonitor *monitor)
2160 {
2161   GdkWin32Surface *impl;
2162   GdkRectangle rect;
2163 
2164   impl = GDK_WIN32_SURFACE (window);
2165 
2166   impl->snap_state = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED;
2167 
2168   if (impl->snap_stash == NULL)
2169     return;
2170 
2171   gdk_win32_monitor_get_workarea (monitor, &rect);
2172 
2173   GDK_NOTE (MISC, g_print ("Monitor work area %d x %d @ %d : %d\n", rect.width, rect.height, rect.x, rect.y));
2174 
2175   if (rect.width >= impl->snap_stash_int->width &&
2176       rect.height >= impl->snap_stash_int->height)
2177     {
2178       /* If the window fits into new work area without resizing it,
2179        * place it into new work area without resizing it.
2180        */
2181       double left, right, up, down, hratio, vratio;
2182       double hscale, vscale;
2183       double new_left, new_up;
2184 
2185       left = impl->snap_stash->x;
2186       right = 1.0 - (impl->snap_stash->x + impl->snap_stash->width);
2187       up = impl->snap_stash->y;
2188       down = 1.0 - (impl->snap_stash->y + impl->snap_stash->height);
2189       hscale = 1.0;
2190 
2191       if (right > 0.001)
2192         {
2193           hratio = left / right;
2194           hscale = hratio / (1.0 + hratio);
2195         }
2196 
2197       new_left = (double) (rect.width - impl->snap_stash_int->width) * hscale;
2198 
2199       vscale = 1.0;
2200 
2201       if (down > 0.001)
2202         {
2203           vratio = up / down;
2204           vscale = vratio / (1.0 + vratio);
2205         }
2206 
2207       new_up = (double) (rect.height - impl->snap_stash_int->height) * vscale;
2208 
2209       rect.x = round (rect.x + new_left);
2210       rect.y = round (rect.y + new_up);
2211       rect.width = impl->snap_stash_int->width;
2212       rect.height = impl->snap_stash_int->height;
2213     }
2214   else
2215     {
2216       /* Calculate actual unsnapped window size based on its
2217        * old relative size. Same for position.
2218        */
2219       rect.x += round (rect.width * impl->snap_stash->x);
2220       rect.y += round (rect.height * impl->snap_stash->y);
2221       rect.width = round (rect.width * impl->snap_stash->width);
2222       rect.height = round (rect.height * impl->snap_stash->height);
2223     }
2224 
2225   GDK_NOTE (MISC, g_print ("Unsnapped window size %d x %d @ %d : %d\n", rect.width, rect.height, rect.x, rect.y));
2226 
2227   gdk_win32_surface_move_resize (window, rect.x, rect.y,
2228                                  rect.width, rect.height);
2229 
2230   g_clear_pointer (&impl->snap_stash, g_free);
2231   g_clear_pointer (&impl->snap_stash_int, g_free);
2232 }
2233 
2234 static void
stash_window(GdkSurface * window,GdkWin32Surface * impl)2235 stash_window (GdkSurface          *window,
2236               GdkWin32Surface *impl)
2237 {
2238   int x, y;
2239   int width, wwidth;
2240   int height, wheight;
2241   WINDOWPLACEMENT placement;
2242   HMONITOR hmonitor;
2243   MONITORINFO hmonitor_info;
2244 
2245   placement.length = sizeof(WINDOWPLACEMENT);
2246 
2247   /* Use W32 API to get unmaximized window size, which GDK doesn't remember */
2248   if (!GetWindowPlacement (GDK_SURFACE_HWND (window), &placement))
2249     return;
2250 
2251   /* MSDN is very vague, but in practice rcNormalPosition is the same as GetWindowRect(),
2252    * only with adjustments for toolbars (which creates rather weird coordinate space issues).
2253    * We need to get monitor info and apply workarea vs monitorarea diff to turn
2254    * these into screen coordinates proper.
2255    */
2256   hmonitor = MonitorFromWindow (GDK_SURFACE_HWND (window), MONITOR_DEFAULTTONEAREST);
2257   hmonitor_info.cbSize = sizeof (hmonitor_info);
2258 
2259   if (!GetMonitorInfoA (hmonitor, &hmonitor_info))
2260     return;
2261 
2262   if (impl->snap_stash == NULL)
2263     impl->snap_stash = g_new0 (GdkRectangleDouble, 1);
2264 
2265   if (impl->snap_stash_int == NULL)
2266     impl->snap_stash_int = g_new0 (GdkRectangle, 1);
2267 
2268   GDK_NOTE (MISC, g_print ("monitor work area  %ld x %ld @ %ld : %ld\n",
2269                            (hmonitor_info.rcWork.right - hmonitor_info.rcWork.left) / impl->surface_scale,
2270                            (hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top) / impl->surface_scale,
2271                            hmonitor_info.rcWork.left,
2272                            hmonitor_info.rcWork.top));
2273   GDK_NOTE (MISC, g_print ("monitor      area  %ld x %ld @ %ld : %ld\n",
2274                            (hmonitor_info.rcMonitor.right - hmonitor_info.rcMonitor.left) / impl->surface_scale,
2275                            (hmonitor_info.rcMonitor.bottom - hmonitor_info.rcMonitor.top) / impl->surface_scale,
2276                            hmonitor_info.rcMonitor.left,
2277                            hmonitor_info.rcMonitor.top));
2278   GDK_NOTE (MISC, g_print ("window  work place %ld x %ld @ %ld : %ld\n",
2279                            (placement.rcNormalPosition.right - placement.rcNormalPosition.left) / impl->surface_scale,
2280                            (placement.rcNormalPosition.bottom - placement.rcNormalPosition.top) / impl->surface_scale,
2281                            placement.rcNormalPosition.left,
2282                            placement.rcNormalPosition.top));
2283 
2284   width = (placement.rcNormalPosition.right - placement.rcNormalPosition.left) / impl->surface_scale;
2285   height = (placement.rcNormalPosition.bottom - placement.rcNormalPosition.top) / impl->surface_scale;
2286   x = (placement.rcNormalPosition.left - hmonitor_info.rcMonitor.left) / impl->surface_scale;
2287   y = (placement.rcNormalPosition.top - hmonitor_info.rcMonitor.top) / impl->surface_scale;
2288 
2289   wwidth = (hmonitor_info.rcWork.right - hmonitor_info.rcWork.left) / impl->surface_scale;
2290   wheight = (hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top) / impl->surface_scale;
2291 
2292   impl->snap_stash->x = (double) (x) / (double) (wwidth);
2293   impl->snap_stash->y = (double) (y) / (double) (wheight);
2294   impl->snap_stash->width = (double) width / (double) (wwidth);
2295   impl->snap_stash->height = (double) height / (double) (wheight);
2296 
2297   impl->snap_stash_int->x = x;
2298   impl->snap_stash_int->y = y;
2299   impl->snap_stash_int->width = width;
2300   impl->snap_stash_int->height = height;
2301 
2302   GDK_NOTE (MISC, g_print ("Stashed window %d x %d @ %d : %d as %f x %f @ %f : %f\n",
2303                            width, height, x, y,
2304                            impl->snap_stash->width, impl->snap_stash->height, impl->snap_stash->x, impl->snap_stash->y));
2305 }
2306 
2307 static void
snap_up(GdkSurface * window)2308 snap_up (GdkSurface *window)
2309 {
2310   SHORT maxysize;
2311   int x, y;
2312   int width, height;
2313   GdkWin32Surface *impl;
2314 
2315   impl = GDK_WIN32_SURFACE (window);
2316 
2317   impl->snap_state = GDK_WIN32_AEROSNAP_STATE_FULLUP;
2318 
2319   stash_window (window, impl);
2320 
2321   maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN) / impl->surface_scale;
2322   x = y = 0;
2323   width = gdk_surface_get_width (window);
2324 
2325   y = 0;
2326   height = maxysize;
2327 
2328   x = x - impl->shadow.left;
2329   y = y - impl->shadow.top;
2330   width += impl->shadow_x;
2331   height += impl->shadow_y;
2332 
2333   /* XXX: FIXME, AeroSnap snap_up() not really working well,
2334    *
2335    *    * The snap_up() puts the window at the top left corner.
2336    *    * Without the following call, the height maximizes but we see a spew of
2337    *      "GdkToplevelSize: geometry size (x,y) exceeds bounds" warnings
2338    */
2339   compute_toplevel_size (window, TRUE, &width, &height);
2340 
2341   gdk_win32_surface_move_resize (window, x, y, width, height);
2342 }
2343 
2344 static void
snap_left(GdkSurface * window,GdkMonitor * monitor,GdkMonitor * snap_monitor)2345 snap_left (GdkSurface  *window,
2346            GdkMonitor *monitor,
2347            GdkMonitor *snap_monitor)
2348 {
2349   GdkRectangle rect;
2350   GdkWin32Surface *impl;
2351 
2352   impl = GDK_WIN32_SURFACE (window);
2353 
2354   impl->snap_state = GDK_WIN32_AEROSNAP_STATE_HALFLEFT;
2355 
2356   gdk_win32_monitor_get_workarea (snap_monitor, &rect);
2357 
2358   stash_window (window, impl);
2359 
2360   rect.width = rect.width / 2;
2361 
2362   rect.x = rect.x - impl->shadow.left;
2363   rect.y = rect.y - impl->shadow.top;
2364   rect.width = rect.width + impl->shadow_x;
2365   rect.height = rect.height + impl->shadow_y;
2366 
2367   gdk_win32_surface_move_resize (window,
2368                                  rect.x, rect.y,
2369                                  rect.width, rect.height);
2370 }
2371 
2372 static void
snap_right(GdkSurface * window,GdkMonitor * monitor,GdkMonitor * snap_monitor)2373 snap_right (GdkSurface  *window,
2374             GdkMonitor *monitor,
2375             GdkMonitor *snap_monitor)
2376 {
2377   GdkRectangle rect;
2378   GdkWin32Surface *impl;
2379 
2380   impl = GDK_WIN32_SURFACE (window);
2381 
2382   impl->snap_state = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT;
2383 
2384   gdk_win32_monitor_get_workarea (snap_monitor, &rect);
2385 
2386   stash_window (window, impl);
2387 
2388   rect.width = rect.width / 2;
2389   rect.x += rect.width;
2390 
2391   rect.x = rect.x - impl->shadow.left;
2392   rect.y = rect.y - impl->shadow.top;
2393   rect.width = rect.width + impl->shadow_x;
2394   rect.height = rect.height + impl->shadow_y;
2395 
2396   gdk_win32_surface_move_resize (window,
2397                                  rect.x, rect.y,
2398                                  rect.width, rect.height);
2399 }
2400 
2401 static void
2402 gdk_win32_surface_maximize (GdkSurface *window);
2403 static void
2404 gdk_win32_surface_unmaximize (GdkSurface *window);
2405 static void
2406 gdk_win32_surface_minimize (GdkSurface *window);
2407 
2408 void
_gdk_win32_surface_handle_aerosnap(GdkSurface * window,GdkWin32AeroSnapCombo combo)2409 _gdk_win32_surface_handle_aerosnap (GdkSurface            *window,
2410                                    GdkWin32AeroSnapCombo combo)
2411 {
2412   GdkWin32Surface *impl;
2413   GdkDisplay *display;
2414   GListModel *monitors;
2415   int n_monitors;
2416   GdkToplevelState surface_state = gdk_toplevel_get_state (GDK_TOPLEVEL (window));
2417   gboolean minimized = surface_state & GDK_TOPLEVEL_STATE_MINIMIZED;
2418   gboolean maximized = surface_state & GDK_TOPLEVEL_STATE_MAXIMIZED;
2419   gboolean halfsnapped;
2420   GdkMonitor *monitor;
2421 
2422   impl = GDK_WIN32_SURFACE (window);
2423   display = gdk_surface_get_display (window);
2424   monitors = gdk_display_get_monitors (display);
2425   n_monitors = g_list_model_get_n_items (monitors);
2426   monitor = gdk_display_get_monitor_at_surface (display, window);
2427 
2428   if (minimized && maximized)
2429     minimized = FALSE;
2430 
2431   halfsnapped = (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT ||
2432                  impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT ||
2433                  impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP);
2434 
2435   switch (combo)
2436     {
2437     case GDK_WIN32_AEROSNAP_COMBO_NOTHING:
2438       /* Do nothing */
2439       break;
2440     case GDK_WIN32_AEROSNAP_COMBO_UP:
2441       if (!maximized)
2442         {
2443 	  unsnap (window, monitor);
2444           gdk_win32_surface_maximize (window);
2445         }
2446       break;
2447     case GDK_WIN32_AEROSNAP_COMBO_DOWN:
2448     case GDK_WIN32_AEROSNAP_COMBO_SHIFTDOWN:
2449       if (maximized)
2450         {
2451 	  gdk_win32_surface_unmaximize (window);
2452 	  unsnap (window, monitor);
2453         }
2454       else if (halfsnapped)
2455 	unsnap (window, monitor);
2456       else if (!minimized)
2457 	gdk_win32_surface_minimize (window);
2458       break;
2459     case GDK_WIN32_AEROSNAP_COMBO_LEFT:
2460       if (maximized)
2461         gdk_win32_surface_unmaximize (window);
2462 
2463       if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED ||
2464 	  impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP)
2465 	{
2466 	  unsnap (window, monitor);
2467 	  snap_left (window, monitor, monitor);
2468 	}
2469       else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT)
2470 	{
2471           GdkMonitor *other;
2472 
2473 	  unsnap (window, monitor);
2474           if (gdk_win32_display_get_primary_monitor (monitor->display) == monitor)
2475             other = g_object_ref (monitor);
2476           else
2477             other = g_list_model_get_item (monitors, n_monitors - 1);
2478 	  snap_right (window, monitor, other);
2479           g_object_unref (other);
2480 	}
2481       else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT)
2482 	{
2483 	  unsnap (window, monitor);
2484 	}
2485       break;
2486     case GDK_WIN32_AEROSNAP_COMBO_RIGHT:
2487       if (maximized)
2488         gdk_win32_surface_unmaximize (window);
2489 
2490       if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED ||
2491 	  impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP)
2492 	{
2493 	  unsnap (window, monitor);
2494 	  snap_right (window, monitor, monitor);
2495 	}
2496       else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT)
2497 	{
2498 	  unsnap (window, monitor);
2499 	}
2500       else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT)
2501 	{
2502           GdkMonitor *other;
2503 	  int i;
2504 
2505 	  unsnap (window, monitor);
2506           for (i = 0; i < n_monitors; i++)
2507             {
2508               other = g_list_model_get_item (monitors, i);
2509               g_object_unref (other);
2510               if (monitor == other)
2511                 break;
2512             }
2513 
2514           other = g_list_model_get_item (monitors, (i + 1) % n_monitors);
2515           snap_left (window, monitor, other);
2516           g_object_unref (other);
2517 	}
2518       break;
2519     case GDK_WIN32_AEROSNAP_COMBO_SHIFTUP:
2520       if (!maximized &&
2521           impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED)
2522 	{
2523 	  snap_up (window);
2524 	}
2525       break;
2526     case GDK_WIN32_AEROSNAP_COMBO_SHIFTLEFT:
2527     case GDK_WIN32_AEROSNAP_COMBO_SHIFTRIGHT:
2528       /* No implementation needed at the moment */
2529       break;
2530     }
2531 }
2532 
2533 static void
apply_snap(GdkSurface * window,GdkWin32AeroSnapState snap)2534 apply_snap (GdkSurface             *window,
2535             GdkWin32AeroSnapState  snap)
2536 {
2537   GdkMonitor *monitor;
2538   GdkDisplay *display;
2539 
2540   display = gdk_surface_get_display (window);
2541   monitor = gdk_display_get_monitor_at_surface (display, window);
2542 
2543   switch (snap)
2544     {
2545     case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED:
2546       break;
2547     case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE:
2548       unsnap (window, monitor);
2549       gdk_win32_surface_maximize (window);
2550       break;
2551     case GDK_WIN32_AEROSNAP_STATE_HALFLEFT:
2552       unsnap (window, monitor);
2553       snap_left (window, monitor, monitor);
2554       break;
2555     case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT:
2556       unsnap (window, monitor);
2557       snap_right (window, monitor, monitor);
2558       break;
2559     case GDK_WIN32_AEROSNAP_STATE_FULLUP:
2560       snap_up (window);
2561       break;
2562     }
2563 }
2564 
2565 /* Registers a dumb window class. This window
2566  * has DefWindowProc() for a window procedure and
2567  * does not do anything that GdkSurface-bound HWNDs do.
2568  */
2569 static ATOM
RegisterGdkDumbClass()2570 RegisterGdkDumbClass ()
2571 {
2572   static ATOM klassDUMB = 0;
2573   static WNDCLASSEXW wcl;
2574   ATOM klass = 0;
2575 
2576   wcl.cbSize = sizeof (WNDCLASSEX);
2577   wcl.style = 0; /* DON'T set CS_<H,V>REDRAW. It causes total redraw
2578                   * on WM_SIZE and WM_MOVE. Flicker, Performance!
2579                   */
2580   wcl.lpfnWndProc = DefWindowProcW;
2581   wcl.cbClsExtra = 0;
2582   wcl.cbWndExtra = 0;
2583   wcl.hInstance = _gdk_dll_hinstance;
2584   wcl.hIcon = 0;
2585   wcl.hIconSm = 0;
2586   wcl.lpszMenuName = NULL;
2587   wcl.hbrBackground = NULL;
2588   wcl.hCursor = LoadCursor (NULL, IDC_ARROW);
2589   wcl.style |= CS_OWNDC;
2590   wcl.lpszClassName = L"gdkSurfaceDumb";
2591 
2592   if (klassDUMB == 0)
2593     klassDUMB = RegisterClassExW (&wcl);
2594 
2595   klass = klassDUMB;
2596 
2597   if (klass == 0)
2598     {
2599       WIN32_API_FAILED ("RegisterClassExW");
2600       g_error ("That is a fatal error");
2601     }
2602 
2603   return klass;
2604 }
2605 
2606 static gboolean
ensure_snap_indicator_exists(GdkW32DragMoveResizeContext * context)2607 ensure_snap_indicator_exists (GdkW32DragMoveResizeContext *context)
2608 {
2609   if (context->shape_indicator == NULL)
2610     {
2611       HWND handle;
2612       ATOM klass;
2613       klass = RegisterGdkDumbClass ();
2614 
2615       handle = CreateWindowExW (WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE,
2616                                 MAKEINTRESOURCEW (klass),
2617                                 L"",
2618                                 WS_POPUP,
2619                                 0,
2620                                 0,
2621                                 0, 0,
2622                                 NULL,
2623                                 NULL,
2624                                 _gdk_dll_hinstance,
2625                                 NULL);
2626 
2627       context->shape_indicator = handle;
2628     }
2629 
2630   return context->shape_indicator != NULL;
2631 }
2632 
2633 static gboolean
ensure_snap_indicator_surface(GdkW32DragMoveResizeContext * context,int width,int height,guint scale)2634 ensure_snap_indicator_surface (GdkW32DragMoveResizeContext *context,
2635                           int                          width,
2636                           int                          height,
2637                           guint                        scale)
2638 {
2639   if (context->indicator_surface != NULL &&
2640       (context->indicator_surface_width < width ||
2641        context->indicator_surface_height < height))
2642     {
2643       cairo_surface_destroy (context->indicator_surface);
2644       context->indicator_surface = NULL;
2645     }
2646 
2647   if (context->indicator_surface == NULL)
2648     context->indicator_surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32,
2649                                                                       width * scale,
2650                                                                       height * scale);
2651 
2652   if (cairo_surface_status (context->indicator_surface) != CAIRO_STATUS_SUCCESS)
2653     {
2654       cairo_surface_destroy (context->indicator_surface);
2655       context->indicator_surface = NULL;
2656 
2657       return FALSE;
2658     }
2659 
2660   return TRUE;
2661 }
2662 
2663 /* Indicator is drawn with some inward offset, so that it does
2664  * not hug screen edges.
2665  */
2666 static void
adjust_indicator_rectangle(GdkRectangle * rect,gboolean inward)2667 adjust_indicator_rectangle (GdkRectangle *rect,
2668                             gboolean      inward)
2669 {
2670   double inverter;
2671   const int gap = AEROSNAP_INDICATOR_EDGE_GAP;
2672 #if defined(MORE_AEROSNAP_DEBUGGING)
2673   GdkRectangle cache = *rect;
2674 #endif
2675 
2676   if (inward)
2677     inverter = 1.0;
2678   else
2679     inverter = -1.0;
2680 
2681   rect->x += (gap * inverter);
2682   rect->y += (gap * inverter);
2683   rect->width -= (gap * 2 * inverter);
2684   rect->height -= (gap * 2 * inverter);
2685 
2686 #if defined(MORE_AEROSNAP_DEBUGGING)
2687   GDK_NOTE (MISC, g_print ("Adjusted %d x %d @ %d : %d -> %d x %d @ %d : %d\n",
2688                            cache.width, cache.height, cache.x, cache.y,
2689                            rect->width, rect->height, rect->x, rect->y));
2690 #endif
2691 }
2692 
2693 static void
rounded_rectangle(cairo_t * cr,int x,int y,int width,int height,double radius,double line_width,GdkRGBA * fill,GdkRGBA * outline)2694 rounded_rectangle (cairo_t  *cr,
2695                    int       x,
2696                    int       y,
2697                    int       width,
2698                    int       height,
2699                    double    radius,
2700                    double    line_width,
2701                    GdkRGBA  *fill,
2702                    GdkRGBA  *outline)
2703 {
2704   double degrees = M_PI / 180.0;
2705 
2706   if (fill == NULL && outline == NULL)
2707     return;
2708 
2709   cairo_save (cr);
2710   cairo_new_sub_path (cr);
2711   cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees);
2712   cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees);
2713   cairo_arc (cr, (x + radius), y + height - radius, radius, 90 * degrees, 180 * degrees);
2714   cairo_arc (cr, (x + radius), (y + radius), radius, 180 * degrees, 270 * degrees);
2715   cairo_close_path (cr);
2716 
2717   if (fill)
2718     {
2719       cairo_set_source_rgba (cr, fill->red, fill->green, fill->blue, fill->alpha);
2720 
2721       if (outline)
2722         cairo_fill_preserve (cr);
2723       else
2724         cairo_fill (cr);
2725     }
2726 
2727   if (outline)
2728     {
2729       cairo_set_source_rgba (cr, outline->red, outline->green, outline->blue, outline->alpha);
2730       cairo_set_line_width (cr, line_width);
2731       cairo_stroke (cr);
2732     }
2733 
2734   cairo_restore (cr);
2735 }
2736 
2737 /* Translates linear animation scale into some kind of curve */
2738 static double
curve(double val)2739 curve (double val)
2740 {
2741   /* TODO: try different curves. For now it's just linear */
2742   return val;
2743 }
2744 
2745 static gboolean
draw_indicator(GdkW32DragMoveResizeContext * context,gint64 timestamp)2746 draw_indicator (GdkW32DragMoveResizeContext *context,
2747                 gint64                       timestamp)
2748 {
2749   cairo_t *cr;
2750   GdkRGBA outline = {0, 0, 1.0, 1.0};
2751   GdkRGBA fill = {0, 0, 1.0, 0.8};
2752   GdkRectangle current_rect;
2753   gint64 current_time = g_get_monotonic_time ();
2754   double animation_progress;
2755   gboolean last_draw;
2756   double line_width;
2757   double corner_radius;
2758   gint64 animation_duration;
2759   GdkWin32Surface *impl = GDK_WIN32_SURFACE (context->window);
2760 
2761   line_width = AEROSNAP_INDICATOR_LINE_WIDTH * impl->surface_scale;
2762   corner_radius = AEROSNAP_INDICATOR_CORNER_RADIUS;
2763   animation_duration = AEROSNAP_INDICATOR_ANIMATION_DURATION;
2764   last_draw = FALSE;
2765 
2766   if (timestamp == 0 &&
2767       current_time - context->indicator_start_time > animation_duration)
2768     {
2769       timestamp = context->indicator_start_time + animation_duration;
2770       last_draw = TRUE;
2771     }
2772 
2773   if (timestamp != 0)
2774     current_time = timestamp;
2775 
2776   animation_progress = (double) (current_time - context->indicator_start_time) / animation_duration;
2777 
2778   if (animation_progress > 1.0)
2779     animation_progress = 1.0;
2780 
2781   if (animation_progress < 0)
2782     animation_progress = 0;
2783 
2784   animation_progress = curve (animation_progress);
2785 
2786   current_rect = context->indicator_start;
2787   current_rect.x += (context->indicator_target.x - context->indicator_start.x) * animation_progress;
2788   current_rect.y += (context->indicator_target.y - context->indicator_start.y) * animation_progress;
2789   current_rect.width += (context->indicator_target.width - context->indicator_start.width) * animation_progress;
2790   current_rect.height += (context->indicator_target.height - context->indicator_start.height) * animation_progress;
2791 
2792   if (context->op == GDK_WIN32_DRAGOP_RESIZE && last_draw)
2793     {
2794       switch (context->edge)
2795         {
2796         case GDK_SURFACE_EDGE_NORTH_WEST:
2797           current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width);
2798           current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height);
2799           break;
2800         case GDK_SURFACE_EDGE_NORTH:
2801           current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height);
2802           break;
2803         case GDK_SURFACE_EDGE_WEST:
2804           current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width);
2805           break;
2806         case GDK_SURFACE_EDGE_SOUTH_WEST:
2807           current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width);
2808           current_rect.y = context->indicator_target.y;
2809           break;
2810         case GDK_SURFACE_EDGE_NORTH_EAST:
2811           current_rect.x = context->indicator_target.x;
2812           current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height);
2813           break;
2814         case GDK_SURFACE_EDGE_SOUTH_EAST:
2815           current_rect.x = context->indicator_target.x;
2816           current_rect.y = context->indicator_target.y;
2817           break;
2818         case GDK_SURFACE_EDGE_SOUTH:
2819           current_rect.y = context->indicator_target.y;
2820           break;
2821         case GDK_SURFACE_EDGE_EAST:
2822           current_rect.x = context->indicator_target.x;
2823           break;
2824         }
2825     }
2826 
2827   cr = cairo_create (context->indicator_surface);
2828   rounded_rectangle (cr,
2829                      (current_rect.x - context->indicator_window_rect.x) * impl->surface_scale,
2830                      (current_rect.y - context->indicator_window_rect.y) * impl->surface_scale,
2831                      current_rect.width * impl->surface_scale,
2832                      current_rect.height * impl->surface_scale,
2833                      corner_radius,
2834                      line_width,
2835                      &fill, &outline);
2836   cairo_destroy (cr);
2837 
2838 #if defined(MORE_AEROSNAP_DEBUGGING)
2839   GDK_NOTE (MISC, g_print ("Indicator is %d x %d @ %d : %d; current time is %" G_GINT64_FORMAT "\n",
2840                            current_rect.width, current_rect.height,
2841                            current_rect.x - context->indicator_window_rect.x,
2842                            current_rect.y - context->indicator_window_rect.y,
2843                            current_time));
2844 #endif
2845 
2846   return last_draw;
2847 }
2848 
2849 static gboolean
redraw_indicator(gpointer user_data)2850 redraw_indicator (gpointer user_data)
2851 {
2852   GdkW32DragMoveResizeContext *context = user_data;
2853   POINT window_position;
2854   SIZE window_size;
2855   BLENDFUNCTION blender;
2856   HDC hdc;
2857   POINT source_point = { 0, 0 };
2858   gboolean last_draw;
2859   double indicator_opacity;
2860   GdkWin32Surface *impl;
2861   gboolean do_source_remove = FALSE;
2862 
2863   indicator_opacity = AEROSNAP_INDICATOR_OPACITY;
2864 
2865   if (GDK_SURFACE_DESTROYED (context->window) ||
2866       !ensure_snap_indicator_exists (context))
2867     {
2868       do_source_remove = TRUE;
2869     }
2870 
2871   impl = GDK_WIN32_SURFACE (context->window);
2872 
2873   if (!ensure_snap_indicator_surface (context,
2874                                       context->indicator_window_rect.width,
2875                                       context->indicator_window_rect.height,
2876                                       impl->surface_scale))
2877     {
2878       do_source_remove = TRUE;
2879     }
2880 
2881   if (do_source_remove)
2882     {
2883       context->timer = 0;
2884       return G_SOURCE_REMOVE;
2885     }
2886 
2887   last_draw = draw_indicator (context, context->draw_timestamp);
2888 
2889   window_position.x = (context->indicator_window_rect.x - _gdk_offset_x) * impl->surface_scale;
2890   window_position.y = (context->indicator_window_rect.y - _gdk_offset_y) * impl->surface_scale;
2891   window_size.cx = context->indicator_window_rect.width * impl->surface_scale;
2892   window_size.cy = context->indicator_window_rect.height * impl->surface_scale;
2893 
2894   blender.BlendOp = AC_SRC_OVER;
2895   blender.BlendFlags = 0;
2896   blender.AlphaFormat = AC_SRC_ALPHA;
2897   blender.SourceConstantAlpha = 255 * indicator_opacity;
2898 
2899   hdc = cairo_win32_surface_get_dc (context->indicator_surface);
2900 
2901   API_CALL (SetWindowPos, (context->shape_indicator,
2902                            GDK_SURFACE_HWND (context->window),
2903                            0, 0, 0, 0,
2904                            SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW | SWP_SHOWWINDOW | SWP_NOACTIVATE));
2905 
2906 #if defined(MORE_AEROSNAP_DEBUGGING)
2907   GDK_NOTE (MISC, g_print ("Indicator window position is %ld x %ld @ %ld : %ld\n",
2908                            window_size.cx, window_size.cy,
2909                            window_position.x, window_position.y));
2910 #endif
2911 
2912   API_CALL (UpdateLayeredWindow, (context->shape_indicator, NULL,
2913                                   &window_position, &window_size,
2914                                   hdc, &source_point,
2915                                   0, &blender, ULW_ALPHA));
2916 
2917   if (last_draw)
2918     context->timer = 0;
2919 
2920   return last_draw ? G_SOURCE_REMOVE : G_SOURCE_CONTINUE;
2921 }
2922 
2923 static GdkRectangle
unity_of_rects(GdkRectangle a,GdkRectangle b)2924 unity_of_rects (GdkRectangle a,
2925                 GdkRectangle b)
2926 {
2927   GdkRectangle u = b;
2928 
2929   if (a.x < u.x)
2930     {
2931       u.width += u.x - a.x;
2932       u.x = a.x;
2933     }
2934 
2935   if (a.y < u.y)
2936     {
2937       u.height += (u.y - a.y);
2938       u.y = a.y;
2939     }
2940 
2941   if (a.x + a.width > u.x + u.width)
2942     u.width += (a.x + a.width) - (u.x + u.width);
2943 
2944   if (a.y + a.height > u.y + u.height)
2945     u.height += (a.y + a.height) - (u.y + u.height);
2946 
2947 #if defined(MORE_AEROSNAP_DEBUGGING)
2948   GDK_NOTE (MISC, g_print ("Unified 2 rects into %d x %d @ %d : %d\n",
2949                            u.width, u.height, u.x, u.y));
2950 #endif
2951 
2952   return u;
2953 }
2954 
2955 static void
start_indicator_drawing(GdkW32DragMoveResizeContext * context,GdkRectangle from,GdkRectangle to,guint scale)2956 start_indicator_drawing (GdkW32DragMoveResizeContext *context,
2957                          GdkRectangle                 from,
2958                          GdkRectangle                 to,
2959                          guint                        scale)
2960 {
2961   GdkRectangle to_adjusted, from_adjusted, from_or_to;
2962   gint64 indicator_animation_tick = AEROSNAP_INDICATOR_ANIMATION_TICK;
2963 
2964   GDK_NOTE (MISC, g_print ("Start drawing snap indicator %d x %d @ %d : %d -> %d x %d @ %d : %d\n",
2965                            from.width * scale, from.height * scale, from.x, from.y, to.width * scale, to.height * scale, to.x, to.y));
2966 
2967   if (GDK_SURFACE_DESTROYED (context->window))
2968     return;
2969 
2970   if (!ensure_snap_indicator_exists (context))
2971     return;
2972 
2973   from_or_to = unity_of_rects (from, to);
2974 
2975   if (!ensure_snap_indicator_surface (context, from_or_to.width, from_or_to.height, scale))
2976     return;
2977 
2978   to_adjusted = to;
2979   adjust_indicator_rectangle (&to_adjusted, TRUE);
2980 
2981   from_adjusted = from;
2982   adjust_indicator_rectangle (&from_adjusted, TRUE);
2983 
2984   context->draw_timestamp = 0;
2985   context->indicator_start = from_adjusted;
2986   context->indicator_target = to_adjusted;
2987   context->indicator_window_rect = from_or_to;
2988   context->indicator_start_time = g_get_monotonic_time ();
2989 
2990   if (context->timer)
2991     {
2992       g_source_remove (context->timer);
2993       context->timer = 0;
2994     }
2995 
2996   context->timer = g_timeout_add_full (G_PRIORITY_DEFAULT,
2997                                        indicator_animation_tick,
2998                                        redraw_indicator,
2999                                        context,
3000                                        NULL);
3001 }
3002 
3003 static void
update_fullup_indicator(GdkSurface * window,GdkW32DragMoveResizeContext * context)3004 update_fullup_indicator (GdkSurface                   *window,
3005                          GdkW32DragMoveResizeContext *context)
3006 {
3007   SHORT maxysize;
3008   GdkRectangle from, to;
3009   GdkRectangle to_adjusted, from_adjusted, from_or_to;
3010   GdkWin32Surface *impl;
3011 
3012   GDK_NOTE (MISC, g_print ("Update fullup indicator\n"));
3013 
3014   if (GDK_SURFACE_DESTROYED (context->window))
3015     return;
3016 
3017   if (context->shape_indicator == NULL)
3018     return;
3019 
3020   impl = GDK_WIN32_SURFACE (window);
3021   maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN);
3022   to.x = to.y = 0;
3023   to.width = gdk_surface_get_width (window);
3024   to.height = gdk_surface_get_height (window);
3025 
3026   to.y = 0;
3027   to.height = maxysize;
3028   from = context->indicator_target;
3029 
3030   if (context->timer == 0)
3031     {
3032       from_adjusted = from;
3033       adjust_indicator_rectangle (&from_adjusted, FALSE);
3034 
3035       GDK_NOTE (MISC, g_print ("Restart fullup animation from %d x %d @ %d : %d -> %d x %d @ %d x %d\n",
3036                                context->indicator_target.width, context->indicator_target.height,
3037                                context->indicator_target.x, context->indicator_target.y,
3038                                to.width, to.height, to.x, to.y));
3039       start_indicator_drawing (context, from_adjusted, to, impl->surface_scale);
3040 
3041       return;
3042     }
3043 
3044   from_or_to = unity_of_rects (from, to);
3045 
3046   to_adjusted = to;
3047   adjust_indicator_rectangle (&to_adjusted, TRUE);
3048 
3049   GDK_NOTE (MISC, g_print ("Retarget fullup animation %d x %d @ %d : %d -> %d x %d @ %d x %d\n",
3050                            context->indicator_target.width, context->indicator_target.height,
3051                            context->indicator_target.x, context->indicator_target.y,
3052                            to_adjusted.width, to_adjusted.height, to_adjusted.x, to_adjusted.y));
3053 
3054   context->indicator_target = to_adjusted;
3055   context->indicator_window_rect = from_or_to;
3056 
3057   ensure_snap_indicator_surface (context, from_or_to.width, from_or_to.height, impl->surface_scale);
3058 }
3059 
3060 static GdkMonitor *
get_monitor_at_point(GdkDisplay * display,int x,int y)3061 get_monitor_at_point (GdkDisplay *display,
3062                       int         x,
3063                       int         y)
3064 {
3065   GListModel *monitors;
3066   GdkMonitor *nearest = NULL;
3067   int nearest_dist = G_MAXINT;
3068   guint i;
3069 
3070   monitors = gdk_display_get_monitors (display);
3071   for (i = 0; i < g_list_model_get_n_items (monitors); i++)
3072     {
3073       GdkMonitor *monitor;
3074       GdkRectangle geometry;
3075       int dist_x, dist_y, dist;
3076 
3077       monitor = g_list_model_get_item (monitors, i);
3078       gdk_monitor_get_geometry (monitor, &geometry);
3079 
3080       if (x < geometry.x)
3081         dist_x = geometry.x - x;
3082       else if (geometry.x + geometry.width <= x)
3083         dist_x = x - (geometry.x + geometry.width) + 1;
3084       else
3085         dist_x = 0;
3086 
3087       if (y < geometry.y)
3088         dist_y = geometry.y - y;
3089       else if (geometry.y + geometry.height <= y)
3090         dist_y = y - (geometry.y + geometry.height) + 1;
3091       else
3092         dist_y = 0;
3093 
3094       dist = dist_x + dist_y;
3095       if (dist < nearest_dist)
3096         {
3097           nearest_dist = dist;
3098           nearest = monitor;
3099         }
3100 
3101       if (x < geometry.x)
3102         dist_x = geometry.x - x;
3103       else if (geometry.x + geometry.width <= x)
3104         dist_x = x - (geometry.x + geometry.width) + 1;
3105       else
3106         dist_x = 0;
3107 
3108       if (y < geometry.y)
3109         dist_y = geometry.y - y;
3110       else if (geometry.y + geometry.height <= y)
3111         dist_y = y - (geometry.y + geometry.height) + 1;
3112       else
3113         dist_y = 0;
3114 
3115       dist = dist_x + dist_y;
3116       if (dist < nearest_dist)
3117         {
3118           nearest_dist = dist;
3119           nearest = monitor;
3120         }
3121 
3122       g_object_unref (monitor);
3123 
3124       if (nearest_dist == 0)
3125         break;
3126     }
3127 
3128   return nearest;
3129 }
3130 
3131 static void
start_indicator(GdkSurface * window,GdkW32DragMoveResizeContext * context,int x,int y,GdkWin32AeroSnapState state)3132 start_indicator (GdkSurface                   *window,
3133                  GdkW32DragMoveResizeContext *context,
3134                  int                          x,
3135                  int                          y,
3136                  GdkWin32AeroSnapState        state)
3137 {
3138   GdkMonitor *monitor;
3139   GdkRectangle workarea;
3140   SHORT maxysize;
3141   GdkRectangle start_size, end_size;
3142   GdkDisplay *display;
3143   GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
3144 
3145   display = gdk_surface_get_display (window);
3146   monitor = get_monitor_at_point (display, x, y);
3147   gdk_win32_monitor_get_workarea (monitor, &workarea);
3148 
3149   maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN) / impl->surface_scale;
3150   start_size.x = start_size.y = 0;
3151   start_size.width = gdk_surface_get_width (window);
3152   start_size.height = gdk_surface_get_height (window);
3153 
3154   end_size = start_size;
3155 
3156   switch (state)
3157     {
3158     case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED:
3159       return;
3160     case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE:
3161       end_size.x = workarea.x;
3162       end_size.y = workarea.y;
3163       end_size.width = workarea.width;
3164       end_size.height = workarea.height;
3165       break;
3166     case GDK_WIN32_AEROSNAP_STATE_HALFLEFT:
3167       end_size.x = workarea.x;
3168       end_size.y = workarea.y;
3169       end_size.width = workarea.width / 2;
3170       end_size.height = workarea.height;
3171       break;
3172     case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT:
3173       end_size.x = (workarea.x + workarea.width / 2);
3174       end_size.y = workarea.y;
3175       end_size.width = workarea.width / 2;
3176       end_size.height = workarea.height;
3177       break;
3178     case GDK_WIN32_AEROSNAP_STATE_FULLUP:
3179       end_size.y = 0;
3180       end_size.height = maxysize;
3181       break;
3182     }
3183 
3184   start_indicator_drawing (context, start_size, end_size, impl->surface_scale);
3185 }
3186 
3187 static void
stop_indicator(GdkSurface * window,GdkW32DragMoveResizeContext * context)3188 stop_indicator (GdkSurface                   *window,
3189                 GdkW32DragMoveResizeContext *context)
3190 {
3191   GDK_NOTE (MISC, g_print ("Stop drawing snap indicator\n"));
3192 
3193   if (context->timer)
3194     {
3195       g_source_remove (context->timer);
3196       context->timer = 0;
3197     }
3198 
3199   API_CALL (SetWindowPos, (context->shape_indicator,
3200                            SWP_NOZORDER_SPECIFIED,
3201                            0, 0, 0, 0,
3202                            SWP_NOZORDER | SWP_NOMOVE |
3203                            SWP_NOSIZE | SWP_NOREDRAW | SWP_HIDEWINDOW | SWP_NOACTIVATE));
3204 }
3205 
3206 static int
point_in_aerosnap_region(int x,int y,AeroSnapEdgeRegion * region)3207 point_in_aerosnap_region (int                 x,
3208                           int                 y,
3209                           AeroSnapEdgeRegion *region)
3210 {
3211   int edge, trigger;
3212 
3213   edge = (x >= region->edge.x &&
3214           y >= region->edge.y &&
3215           x <= region->edge.x + region->edge.width &&
3216           y <= region->edge.y + region->edge.height) ? 1 : 0;
3217   trigger = (x >= region->trigger.x &&
3218              y >= region->trigger.y &&
3219              x <= region->trigger.x + region->trigger.width &&
3220              y <= region->trigger.y + region->trigger.height) ? 1 : 0;
3221   return edge + trigger;
3222 }
3223 
3224 static void
handle_aerosnap_move_resize(GdkSurface * window,GdkW32DragMoveResizeContext * context,int x,int y)3225 handle_aerosnap_move_resize (GdkSurface                   *window,
3226                              GdkW32DragMoveResizeContext *context,
3227                              int                          x,
3228                              int                          y)
3229 {
3230   int i;
3231   AeroSnapEdgeRegion *reg;
3232   int maximize = 0;
3233   int halfleft = 0;
3234   int halfright = 0;
3235   int fullup = 0;
3236   gboolean fullup_edge = FALSE;
3237 
3238   if (context->op == GDK_WIN32_DRAGOP_RESIZE)
3239     switch (context->edge)
3240       {
3241       case GDK_SURFACE_EDGE_NORTH_WEST:
3242       case GDK_SURFACE_EDGE_NORTH_EAST:
3243       case GDK_SURFACE_EDGE_WEST:
3244       case GDK_SURFACE_EDGE_EAST:
3245       case GDK_SURFACE_EDGE_SOUTH_WEST:
3246       case GDK_SURFACE_EDGE_SOUTH_EAST:
3247         break;
3248       case GDK_SURFACE_EDGE_SOUTH:
3249       case GDK_SURFACE_EDGE_NORTH:
3250         fullup_edge = TRUE;
3251         break;
3252       }
3253 
3254   for (i = 0; i < context->maximize_regions->len && maximize == 0; i++)
3255     {
3256       reg = &g_array_index (context->maximize_regions, AeroSnapEdgeRegion, i);
3257       maximize = point_in_aerosnap_region (x, y, reg);
3258     }
3259 
3260   for (i = 0; i < context->halfleft_regions->len && halfleft == 0; i++)
3261     {
3262       reg = &g_array_index (context->halfleft_regions, AeroSnapEdgeRegion, i);
3263       halfleft = point_in_aerosnap_region (x, y, reg);
3264     }
3265 
3266   for (i = 0; i < context->halfright_regions->len && halfright == 0; i++)
3267     {
3268       reg = &g_array_index (context->halfright_regions, AeroSnapEdgeRegion, i);
3269       halfright = point_in_aerosnap_region (x, y, reg);
3270     }
3271 
3272   for (i = 0; i < context->fullup_regions->len && fullup == 0; i++)
3273     {
3274       reg = &g_array_index (context->fullup_regions, AeroSnapEdgeRegion, i);
3275       fullup = point_in_aerosnap_region (x, y, reg);
3276     }
3277 
3278 #if defined(MORE_AEROSNAP_DEBUGGING)
3279   GDK_NOTE (MISC, g_print ("AeroSnap: point %d : %d - max: %d, left %d, right %d, up %d\n",
3280                            x, y, maximize, halfleft, halfright, fullup));
3281 #endif
3282 
3283   if (!context->revealed)
3284     {
3285       if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize == 2)
3286         {
3287           context->revealed = TRUE;
3288           context->current_snap = GDK_WIN32_AEROSNAP_STATE_MAXIMIZE;
3289           start_indicator (window, context, x, y, context->current_snap);
3290         }
3291       else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft == 2)
3292         {
3293           context->revealed = TRUE;
3294           context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFLEFT;
3295           start_indicator (window, context, x, y, context->current_snap);
3296         }
3297       else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright == 2)
3298         {
3299           context->revealed = TRUE;
3300           context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT;
3301           start_indicator (window, context, x, y, context->current_snap);
3302         }
3303       else if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup == 2 && fullup_edge)
3304         {
3305           context->revealed = TRUE;
3306           context->current_snap = GDK_WIN32_AEROSNAP_STATE_FULLUP;
3307           start_indicator (window, context, x, y, context->current_snap);
3308         }
3309 
3310       return;
3311     }
3312 
3313   switch (context->current_snap)
3314     {
3315     case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED:
3316       if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup > 0)
3317         {
3318           context->current_snap = GDK_WIN32_AEROSNAP_STATE_FULLUP;
3319           start_indicator (window, context, x, y, context->current_snap);
3320         }
3321       break;
3322     case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE:
3323       if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize > 0)
3324         break;
3325       if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft > 0)
3326         {
3327           context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFLEFT;
3328           start_indicator (window, context, x, y, context->current_snap);
3329         }
3330       else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright > 0)
3331         {
3332           context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT;
3333           start_indicator (window, context, x, y, context->current_snap);
3334         }
3335       else
3336         {
3337           context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED;
3338           stop_indicator (window, context);
3339           context->revealed = FALSE;
3340         }
3341       break;
3342     case GDK_WIN32_AEROSNAP_STATE_HALFLEFT:
3343       if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft > 0)
3344         break;
3345       if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize > 0)
3346         {
3347           context->current_snap = GDK_WIN32_AEROSNAP_STATE_MAXIMIZE;
3348           start_indicator (window, context, x, y, context->current_snap);
3349         }
3350       else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright > 0)
3351         {
3352           context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT;
3353           start_indicator (window, context, x, y, context->current_snap);
3354         }
3355       else
3356         {
3357           context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED;
3358           stop_indicator (window, context);
3359           context->revealed = FALSE;
3360         }
3361       break;
3362     case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT:
3363       if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright > 0)
3364         break;
3365       if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize > 0)
3366         {
3367           context->current_snap = GDK_WIN32_AEROSNAP_STATE_MAXIMIZE;
3368           start_indicator (window, context, x, y, context->current_snap);
3369         }
3370       else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft > 0)
3371         {
3372           context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFLEFT;
3373           start_indicator (window, context, x, y, context->current_snap);
3374         }
3375       else
3376         {
3377           context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED;
3378           stop_indicator (window, context);
3379           context->revealed = FALSE;
3380         }
3381       break;
3382     case GDK_WIN32_AEROSNAP_STATE_FULLUP:
3383       if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup > 0 && fullup_edge)
3384         {
3385           update_fullup_indicator (window, context);
3386           break;
3387         }
3388 
3389       context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED;
3390       stop_indicator (window, context);
3391       break;
3392     }
3393 }
3394 
3395 
3396 static const char *
get_cursor_name_from_op(GdkW32WindowDragOp op,GdkSurfaceEdge edge)3397 get_cursor_name_from_op (GdkW32WindowDragOp op,
3398                          GdkSurfaceEdge      edge)
3399 {
3400   switch (op)
3401     {
3402     case GDK_WIN32_DRAGOP_MOVE:
3403       return "move";
3404     case GDK_WIN32_DRAGOP_RESIZE:
3405       switch (edge)
3406         {
3407         case GDK_SURFACE_EDGE_NORTH_WEST:
3408           return "nw-resize";
3409         case GDK_SURFACE_EDGE_NORTH:
3410           return "n-resize";
3411         case GDK_SURFACE_EDGE_NORTH_EAST:
3412           return "ne-resize";
3413         case GDK_SURFACE_EDGE_WEST:
3414           return "w-resize";
3415         case GDK_SURFACE_EDGE_EAST:
3416           return "e-resize";
3417         case GDK_SURFACE_EDGE_SOUTH_WEST:
3418           return "sw-resize";
3419         case GDK_SURFACE_EDGE_SOUTH:
3420           return "s-resize";
3421         case GDK_SURFACE_EDGE_SOUTH_EAST:
3422           return "se-resize";
3423         }
3424       /* default: warn about unhandled enum values,
3425        * fallthrough to GDK_WIN32_DRAGOP_NONE case
3426        */
3427     case GDK_WIN32_DRAGOP_COUNT:
3428       g_assert_not_reached ();
3429     case GDK_WIN32_DRAGOP_NONE:
3430       return "default";
3431     /* default: warn about unhandled enum values */
3432     }
3433 
3434   g_assert_not_reached ();
3435 
3436   return NULL;
3437 }
3438 
3439 static void
setup_drag_move_resize_context(GdkSurface * window,GdkW32DragMoveResizeContext * context,GdkW32WindowDragOp op,GdkSurfaceEdge edge,GdkDevice * device,int button,double x,double y,guint32 timestamp)3440 setup_drag_move_resize_context (GdkSurface                   *window,
3441                                 GdkW32DragMoveResizeContext *context,
3442                                 GdkW32WindowDragOp           op,
3443                                 GdkSurfaceEdge                edge,
3444                                 GdkDevice                   *device,
3445                                 int                          button,
3446                                 double                       x,
3447                                 double                       y,
3448                                 guint32                      timestamp)
3449 {
3450   RECT rect;
3451   const char *cursor_name;
3452   GdkSurface *pointer_window;
3453   GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
3454   gboolean maximized = gdk_toplevel_get_state (GDK_TOPLEVEL (window)) & GDK_TOPLEVEL_STATE_MAXIMIZED;
3455   int root_x, root_y;
3456 
3457   gdk_win32_surface_get_root_coords (window, x, y, &root_x, &root_y);
3458 
3459   /* Before we drag, we need to undo any maximization or snapping.
3460    * AeroSnap behaviour:
3461    *   If snapped halfleft/halfright:
3462    *     horizontal resize:
3463    *       resize
3464    *       don't unsnap
3465    *       keep stashed unsnapped size intact
3466    *     vertical resize:
3467    *       resize
3468    *       unsnap to new size (merge cached unsnapped state with current
3469    *                           snapped state in such a way that the gripped edge
3470    *                           does not move)
3471    *     diagonal resize:
3472    *       difficult to test (first move is usually either purely
3473    *                          horizontal or purely vertical, in which
3474    *                          case the above behaviour applies)
3475    *   If snapped up:
3476    *     horizontal resize:
3477    *       resize
3478    *       don't unsnap
3479    *       apply new width and x position to unsnapped cache,
3480    *         so that unsnapped window only regains its height
3481    *         and y position, but inherits x and width from
3482    *         the fullup snapped state
3483    *     vertical resize:
3484    *       unsnap to new size (merge cached unsnapped state with current
3485    *                           snapped state in such a way that the gripped edge
3486    *                           does not move)
3487    *
3488    * This implementation behaviour:
3489    *   If snapped halfleft/halfright/fullup:
3490    *     any resize:
3491    *       unsnap to current size, discard cached pre-snap state
3492    *
3493    * TODO: make this implementation behave as AeroSnap on resizes?
3494    * There's also the case where
3495    * a halfleft/halfright window isn't unsnapped when it's
3496    * being moved horizontally, but it's more difficult to implement.
3497    */
3498   if (op == GDK_WIN32_DRAGOP_RESIZE &&
3499       (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT ||
3500        impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT ||
3501        impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP))
3502     {
3503       discard_snapinfo (window);
3504     }
3505   else if (maximized ||
3506            (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT ||
3507             impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT ||
3508             impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP))
3509     {
3510       GdkMonitor *monitor;
3511       int wx, wy, wwidth, wheight;
3512       int swx, swy, swwidth, swheight;
3513       gboolean pointer_outside_of_window;
3514       int offsetx, offsety;
3515       gboolean left_half;
3516       GdkDisplay *display;
3517 
3518       display = gdk_surface_get_display (window);
3519       monitor = gdk_display_get_monitor_at_surface (display, window);
3520       gdk_surface_get_geometry (window, &wx, &wy, &wwidth, &wheight);
3521 
3522       swx = wx;
3523       swy = wy;
3524       swwidth = wwidth;
3525       swheight = wheight;
3526 
3527       /* Subtract window shadow. We don't want pointer to go outside of
3528        * the visible window during drag-move. For drag-resize it's OK.
3529        * Don't take shadow into account if the window is maximized -
3530        * maximized windows don't have shadows.
3531        */
3532       if (op == GDK_WIN32_DRAGOP_MOVE && !maximized)
3533         {
3534           swx += impl->shadow.left / impl->surface_scale;
3535           swy += impl->shadow.top / impl->surface_scale;
3536           swwidth -= impl->shadow_x;
3537           swheight -= impl->shadow_y;
3538         }
3539 
3540       pointer_outside_of_window = root_x < swx || root_x > swx + swwidth ||
3541                                   root_y < swy || root_y > swy + swheight;
3542       /* Calculate the offset of the pointer relative to the window */
3543       offsetx = root_x - swx;
3544       offsety = root_y - swy;
3545 
3546       /* Figure out in which half of the window the pointer is.
3547        * The code currently only concerns itself with horizontal
3548        * dimension (left/right halves).
3549        * There's no upper/lower half, because usually window
3550        * is dragged by its upper half anyway. If that changes, adjust
3551        * accordingly.
3552        */
3553       left_half = (offsetx < swwidth / 2);
3554 
3555       /* Inverse the offset for it to be from the right edge */
3556       if (!left_half)
3557         offsetx = swwidth - offsetx;
3558 
3559       GDK_NOTE (MISC, g_print ("Pointer at %d : %d, this is %d : %d relative to the window's %s\n",
3560                                root_x, root_y, offsetx, offsety,
3561                                left_half ? "left half" : "right half"));
3562 
3563       /* Move window in such a way that on unmaximization/unsnapping the pointer
3564        * is still pointing at the appropriate half of the window,
3565        * with the same offset from the left or right edge. If the new
3566        * window size is too small, and adding that offset puts the pointer
3567        * into the other half or even beyond, move the pointer to the middle.
3568        */
3569       if (!pointer_outside_of_window && maximized)
3570         {
3571           WINDOWPLACEMENT placement;
3572           int unmax_width, unmax_height;
3573           int shadow_unmax_width, shadow_unmax_height;
3574 
3575           placement.length = sizeof (placement);
3576           API_CALL (GetWindowPlacement, (GDK_SURFACE_HWND (window), &placement));
3577 
3578           GDK_NOTE (MISC, g_print ("W32 WM unmaximized window placement is %ld x %ld @ %ld : %ld\n",
3579                                    placement.rcNormalPosition.right - placement.rcNormalPosition.left,
3580                                    placement.rcNormalPosition.bottom - placement.rcNormalPosition.top,
3581                                    placement.rcNormalPosition.left + _gdk_offset_x * impl->surface_scale,
3582                                    placement.rcNormalPosition.top + _gdk_offset_y * impl->surface_scale));
3583 
3584           unmax_width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
3585           unmax_height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
3586 
3587           shadow_unmax_width = unmax_width - impl->shadow_x * impl->surface_scale;
3588           shadow_unmax_height = unmax_height - impl->shadow_y * impl->surface_scale;
3589 
3590           if (offsetx * impl->surface_scale < (shadow_unmax_width / 2) &&
3591               offsety * impl->surface_scale < (shadow_unmax_height / 2))
3592             {
3593               placement.rcNormalPosition.top = (root_y - offsety + impl->shadow.top - _gdk_offset_y) * impl->surface_scale;
3594               placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + unmax_height;
3595 
3596               if (left_half)
3597                 {
3598                   placement.rcNormalPosition.left = (root_x - offsetx + impl->shadow.left - _gdk_offset_x) * impl->surface_scale;
3599                   placement.rcNormalPosition.right = placement.rcNormalPosition.left + unmax_width;
3600                 }
3601               else
3602                 {
3603                   placement.rcNormalPosition.right = (root_x + offsetx + impl->shadow.right - _gdk_offset_x) * impl->surface_scale;
3604                   placement.rcNormalPosition.left = placement.rcNormalPosition.right - unmax_width;
3605                 }
3606             }
3607           else
3608             {
3609               placement.rcNormalPosition.left = (root_x * impl->surface_scale) -
3610                                                 (unmax_width / 2) -
3611                                                 (_gdk_offset_x * impl->surface_scale);
3612 
3613               if (offsety * impl->surface_scale < shadow_unmax_height / 2)
3614                 placement.rcNormalPosition.top = (root_y - offsety + impl->shadow.top - _gdk_offset_y) * impl->surface_scale;
3615               else
3616                 placement.rcNormalPosition.top = (root_y * impl->surface_scale) -
3617                                                  (unmax_height / 2) -
3618                                                  (_gdk_offset_y * impl->surface_scale);
3619 
3620               placement.rcNormalPosition.right = placement.rcNormalPosition.left + unmax_width;
3621               placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + unmax_height;
3622             }
3623 
3624           GDK_NOTE (MISC, g_print ("Unmaximized window will be at %ld : %ld\n",
3625                                    placement.rcNormalPosition.left + _gdk_offset_x * impl->surface_scale,
3626                                    placement.rcNormalPosition.top + _gdk_offset_y * impl->surface_scale));
3627 
3628           API_CALL (SetWindowPlacement, (GDK_SURFACE_HWND (window), &placement));
3629         }
3630       else if (!pointer_outside_of_window && impl->snap_stash_int)
3631         {
3632           GdkRectangle new_pos;
3633           GdkRectangle snew_pos;
3634 
3635           new_pos.width = impl->snap_stash_int->width;
3636           new_pos.height = impl->snap_stash_int->height;
3637           snew_pos = new_pos;
3638 
3639           if (op == GDK_WIN32_DRAGOP_MOVE)
3640             {
3641               snew_pos.width -= impl->shadow_x;
3642               snew_pos.height -= impl->shadow_y;
3643             }
3644 
3645           if (offsetx < snew_pos.width / 2 && offsety < snew_pos.height / 2)
3646             {
3647               new_pos.y = root_y - offsety + impl->shadow.top / impl->surface_scale;
3648 
3649               if (left_half)
3650                 new_pos.x = root_x - offsetx + impl->shadow.left / impl->surface_scale;
3651               else
3652                 new_pos.x = root_x + offsetx + impl->shadow.left / impl->surface_scale - new_pos.width;
3653             }
3654           else
3655             {
3656               new_pos.x = root_x - new_pos.width / 2;
3657               new_pos.y = root_y - new_pos.height / 2;
3658             }
3659 
3660           GDK_NOTE (MISC, g_print ("Unsnapped window to %d : %d\n",
3661                                    new_pos.x, new_pos.y));
3662           discard_snapinfo (window);
3663           gdk_win32_surface_move_resize (window, new_pos.x, new_pos.y,
3664                                          new_pos.width, new_pos.height);
3665         }
3666 
3667 
3668       if (maximized)
3669         gdk_win32_surface_unmaximize (window);
3670       else
3671         unsnap (window, monitor);
3672 
3673       if (pointer_outside_of_window)
3674         {
3675           /* Pointer outside of the window, move pointer into window */
3676           GDK_NOTE (MISC, g_print ("Pointer at %d : %d is outside of %d x %d @ %d : %d, move it to %d : %d\n",
3677                                    root_x, root_y, wwidth, wheight, wx, wy, wx + wwidth / 2, wy + wheight / 2));
3678           root_x = wx + wwidth / 2;
3679           /* This is Gnome behaviour. Windows WM would put the pointer
3680            * in the middle of the titlebar, but GDK doesn't know where
3681            * the titlebar is, if any.
3682            */
3683           root_y = wy + wheight / 2;
3684           SetCursorPos (root_x - _gdk_offset_x, root_y - _gdk_offset_y);
3685         }
3686     }
3687 
3688   _gdk_win32_get_window_rect (window, &rect);
3689 
3690   cursor_name = get_cursor_name_from_op (op, edge);
3691 
3692   context->cursor = gdk_cursor_new_from_name (cursor_name, NULL);
3693 
3694   pointer_window = window;
3695 
3696   /* Note: This triggers a WM_CAPTURECHANGED, which will trigger
3697    * gdk_win32_surface_end_move_resize_drag(), which will end
3698    * our op before it even begins, but only if context->op is not NONE.
3699    * This is why we first do the grab, *then* set the op.
3700    */
3701   gdk_device_grab (device, pointer_window,
3702                    FALSE,
3703                    GDK_ALL_EVENTS_MASK,
3704                    context->cursor,
3705                    timestamp);
3706 
3707   context->window = g_object_ref (window);
3708   context->op = op;
3709   context->edge = edge;
3710   context->device = device;
3711   context->button = button;
3712   context->start_root_x = root_x;
3713   context->start_root_y = root_y;
3714   context->timestamp = timestamp;
3715   context->start_rect = rect;
3716 
3717   context->shape_indicator = NULL;
3718   context->revealed = FALSE;
3719   context->halfleft_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion));
3720   context->halfright_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion));
3721   context->maximize_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion));
3722   context->fullup_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion));
3723 
3724   calculate_aerosnap_regions (context);
3725 
3726   GDK_NOTE (EVENTS,
3727             g_print ("begin drag moveresize: window %p, toplevel %p, "
3728                      "op %u, edge %d, device %p, "
3729                      "button %d, coord %d:%d, time %u\n",
3730                      pointer_window, window,
3731                      context->op, context->edge, context->device,
3732                      context->button, context->start_root_x,
3733                      context->start_root_y, context->timestamp));
3734 }
3735 
3736 void
gdk_win32_surface_end_move_resize_drag(GdkSurface * window)3737 gdk_win32_surface_end_move_resize_drag (GdkSurface *window)
3738 {
3739   GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
3740   GdkW32DragMoveResizeContext *context = &impl->drag_move_resize_context;
3741 
3742   if (context->op == GDK_WIN32_DRAGOP_RESIZE)
3743     _gdk_win32_surface_invalidate_egl_framebuffer (window);
3744 
3745   context->op = GDK_WIN32_DRAGOP_NONE;
3746 
3747   gdk_device_ungrab (context->device, GDK_CURRENT_TIME);
3748 
3749   g_clear_object (&context->cursor);
3750 
3751   context->revealed = FALSE;
3752 
3753   if (context->timer)
3754     {
3755       g_source_remove (context->timer);
3756       context->timer = 0;
3757     }
3758 
3759   g_clear_object (&context->window);
3760 
3761   if (context->indicator_surface)
3762     {
3763       cairo_surface_destroy (context->indicator_surface);
3764       context->indicator_surface = NULL;
3765     }
3766 
3767   if (context->shape_indicator)
3768     {
3769       stop_indicator (window, context);
3770       DestroyWindow (context->shape_indicator);
3771       context->shape_indicator = NULL;
3772     }
3773 
3774   g_clear_pointer (&context->halfleft_regions, g_array_unref);
3775   g_clear_pointer (&context->halfright_regions, g_array_unref);
3776   g_clear_pointer (&context->maximize_regions, g_array_unref);
3777   g_clear_pointer (&context->fullup_regions, g_array_unref);
3778 
3779   GDK_NOTE (EVENTS,
3780             g_print ("end drag moveresize: window %p, toplevel %p,"
3781                      "op %u, edge %d, device %p, "
3782                      "button %d, coord %d:%d, time %u\n",
3783                      window, window,
3784                      context->op, context->edge, context->device,
3785                      context->button, context->start_root_x,
3786                      context->start_root_y, context->timestamp));
3787 
3788   if (context->current_snap != GDK_WIN32_AEROSNAP_STATE_UNDETERMINED)
3789     apply_snap (window, context->current_snap);
3790 
3791   context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED;
3792 }
3793 
3794 static void
gdk_win32_get_window_size_and_position_from_client_rect(GdkSurface * window,RECT * window_rect,SIZE * window_size,POINT * window_position)3795 gdk_win32_get_window_size_and_position_from_client_rect (GdkSurface *window,
3796                                                          RECT      *window_rect,
3797                                                          SIZE      *window_size,
3798                                                          POINT     *window_position)
3799 {
3800   GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
3801 
3802   /* Turn client area into window area */
3803   _gdk_win32_adjust_client_rect (window, window_rect);
3804 
3805   /* Convert GDK screen coordinates to W32 desktop coordinates */
3806   window_rect->left -= _gdk_offset_x * impl->surface_scale;
3807   window_rect->right -= _gdk_offset_x * impl->surface_scale;
3808   window_rect->top -= _gdk_offset_y * impl->surface_scale;
3809   window_rect->bottom -= _gdk_offset_y * impl->surface_scale;
3810 
3811   window_position->x = window_rect->left;
3812   window_position->y = window_rect->top;
3813   window_size->cx = window_rect->right - window_rect->left;
3814   window_size->cy = window_rect->bottom - window_rect->top;
3815 }
3816 
3817 void
gdk_win32_surface_do_move_resize_drag(GdkSurface * window,int x,int y)3818 gdk_win32_surface_do_move_resize_drag (GdkSurface *window,
3819                                       int        x,
3820                                       int        y)
3821 {
3822   RECT rect;
3823   RECT new_rect;
3824   int diffy, diffx;
3825   MINMAXINFO mmi;
3826   GdkWin32Surface *impl;
3827   GdkW32DragMoveResizeContext *context;
3828   int width;
3829   int height;
3830 
3831   impl = GDK_WIN32_SURFACE (window);
3832   context = &impl->drag_move_resize_context;
3833 
3834   if (!_gdk_win32_get_window_rect (window, &rect))
3835     return;
3836 
3837   new_rect = context->start_rect;
3838   diffx = (x - context->start_root_x) * impl->surface_scale;
3839   diffy = (y - context->start_root_y) * impl->surface_scale;
3840 
3841   switch (context->op)
3842     {
3843     case GDK_WIN32_DRAGOP_RESIZE:
3844 
3845       switch (context->edge)
3846         {
3847         case GDK_SURFACE_EDGE_NORTH_WEST:
3848           new_rect.left += diffx;
3849           new_rect.top += diffy;
3850           break;
3851 
3852         case GDK_SURFACE_EDGE_NORTH:
3853           new_rect.top += diffy;
3854           break;
3855 
3856         case GDK_SURFACE_EDGE_NORTH_EAST:
3857           new_rect.right += diffx;
3858           new_rect.top += diffy;
3859           break;
3860 
3861         case GDK_SURFACE_EDGE_WEST:
3862           new_rect.left += diffx;
3863           break;
3864 
3865         case GDK_SURFACE_EDGE_EAST:
3866           new_rect.right += diffx;
3867           break;
3868 
3869         case GDK_SURFACE_EDGE_SOUTH_WEST:
3870           new_rect.left += diffx;
3871           new_rect.bottom += diffy;
3872           break;
3873 
3874         case GDK_SURFACE_EDGE_SOUTH:
3875           new_rect.bottom += diffy;
3876           break;
3877 
3878         case GDK_SURFACE_EDGE_SOUTH_EAST:
3879         default:
3880           new_rect.right += diffx;
3881           new_rect.bottom += diffy;
3882           break;
3883         }
3884 
3885       /* When handling WM_GETMINMAXINFO, mmi is already populated
3886        * by W32 WM and we apply our stuff on top of that.
3887        * Here it isn't, so we should at least clear it.
3888        */
3889       memset (&mmi, 0, sizeof (mmi));
3890 
3891       if (!_gdk_win32_surface_fill_min_max_info (window, &mmi))
3892         break;
3893 
3894       width = new_rect.right - new_rect.left;
3895       height = new_rect.bottom - new_rect.top;
3896 
3897       if (width > mmi.ptMaxTrackSize.x)
3898         {
3899           switch (context->edge)
3900             {
3901             case GDK_SURFACE_EDGE_NORTH_WEST:
3902             case GDK_SURFACE_EDGE_WEST:
3903             case GDK_SURFACE_EDGE_SOUTH_WEST:
3904               new_rect.left = new_rect.right - mmi.ptMaxTrackSize.x;
3905               break;
3906 
3907             case GDK_SURFACE_EDGE_NORTH_EAST:
3908             case GDK_SURFACE_EDGE_EAST:
3909             case GDK_SURFACE_EDGE_SOUTH_EAST:
3910             default:
3911               new_rect.right = new_rect.left + mmi.ptMaxTrackSize.x;
3912               break;
3913             }
3914         }
3915       else if (width < mmi.ptMinTrackSize.x)
3916         {
3917           switch (context->edge)
3918             {
3919             case GDK_SURFACE_EDGE_NORTH_WEST:
3920             case GDK_SURFACE_EDGE_WEST:
3921             case GDK_SURFACE_EDGE_SOUTH_WEST:
3922               new_rect.left = new_rect.right - mmi.ptMinTrackSize.x;
3923               break;
3924 
3925             case GDK_SURFACE_EDGE_NORTH_EAST:
3926             case GDK_SURFACE_EDGE_EAST:
3927             case GDK_SURFACE_EDGE_SOUTH_EAST:
3928             default:
3929               new_rect.right = new_rect.left + mmi.ptMinTrackSize.x;
3930               break;
3931             }
3932         }
3933 
3934       if (height > mmi.ptMaxTrackSize.y)
3935         {
3936           switch (context->edge)
3937             {
3938             case GDK_SURFACE_EDGE_NORTH_WEST:
3939             case GDK_SURFACE_EDGE_NORTH:
3940             case GDK_SURFACE_EDGE_NORTH_EAST:
3941               new_rect.top = new_rect.bottom - mmi.ptMaxTrackSize.y;
3942 
3943             case GDK_SURFACE_EDGE_SOUTH_WEST:
3944             case GDK_SURFACE_EDGE_SOUTH:
3945             case GDK_SURFACE_EDGE_SOUTH_EAST:
3946             default:
3947               new_rect.bottom = new_rect.top + mmi.ptMaxTrackSize.y;
3948               break;
3949             }
3950         }
3951       else if (height < mmi.ptMinTrackSize.y)
3952         {
3953           switch (context->edge)
3954             {
3955             case GDK_SURFACE_EDGE_NORTH_WEST:
3956             case GDK_SURFACE_EDGE_NORTH:
3957             case GDK_SURFACE_EDGE_NORTH_EAST:
3958               new_rect.top = new_rect.bottom - mmi.ptMinTrackSize.y;
3959 
3960             case GDK_SURFACE_EDGE_SOUTH_WEST:
3961             case GDK_SURFACE_EDGE_SOUTH:
3962             case GDK_SURFACE_EDGE_SOUTH_EAST:
3963             default:
3964               new_rect.bottom = new_rect.top + mmi.ptMinTrackSize.y;
3965               break;
3966             }
3967         }
3968 
3969       break;
3970     case GDK_WIN32_DRAGOP_MOVE:
3971       new_rect.left += diffx;
3972       new_rect.top += diffy;
3973       new_rect.right += diffx;
3974       new_rect.bottom += diffy;
3975       break;
3976     default:
3977       break;
3978     }
3979 
3980   if (context->op == GDK_WIN32_DRAGOP_RESIZE &&
3981       (rect.left != new_rect.left ||
3982        rect.right != new_rect.right ||
3983        rect.top != new_rect.top ||
3984        rect.bottom != new_rect.bottom))
3985     {
3986       if (GDK_IS_TOPLEVEL (window))
3987         {
3988           int scale = impl->surface_scale;
3989 
3990           impl->next_layout.configured_rect = new_rect;
3991           impl->next_layout.configured_width = (new_rect.right - new_rect.left + scale - 1) / scale;
3992           impl->next_layout.configured_height = (new_rect.bottom - new_rect.top + scale - 1) / scale;
3993         }
3994 
3995       context->native_move_resize_pending = TRUE;
3996     }
3997   else if (context->op == GDK_WIN32_DRAGOP_MOVE &&
3998            (rect.left != new_rect.left ||
3999             rect.top != new_rect.top))
4000     {
4001       SIZE window_size;
4002       POINT window_position;
4003 
4004       context->native_move_resize_pending = FALSE;
4005 
4006       gdk_surface_request_layout (window);
4007 
4008       gdk_win32_get_window_size_and_position_from_client_rect (window,
4009                                                               &new_rect,
4010                                                               &window_size,
4011                                                               &window_position);
4012 
4013       API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window),
4014                                SWP_NOZORDER_SPECIFIED,
4015                                window_position.x, window_position.y,
4016                                0, 0,
4017                                SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE));
4018     }
4019 
4020   if (context->op == GDK_WIN32_DRAGOP_RESIZE ||
4021       context->op == GDK_WIN32_DRAGOP_MOVE)
4022     handle_aerosnap_move_resize (window, context, x, y);
4023 
4024   gdk_surface_request_layout (window);
4025 }
4026 
4027 static void
gdk_win32_toplevel_begin_resize(GdkToplevel * toplevel,GdkSurfaceEdge edge,GdkDevice * device,int button,double x,double y,guint32 timestamp)4028 gdk_win32_toplevel_begin_resize (GdkToplevel    *toplevel,
4029                                  GdkSurfaceEdge  edge,
4030                                  GdkDevice      *device,
4031                                  int             button,
4032                                  double          x,
4033                                  double          y,
4034                                  guint32         timestamp)
4035 {
4036   GdkSurface *window = GDK_SURFACE (toplevel);
4037   GdkWin32Surface *impl;
4038 
4039   if (GDK_SURFACE_DESTROYED (window) ||
4040       IsIconic (GDK_SURFACE_HWND (window)))
4041     return;
4042 
4043   /* Tell Windows to start interactively resizing the window by pretending that
4044    * the left pointer button was clicked in the suitable edge or corner. This
4045    * will only work if the button is down when this function is called, and
4046    * will only work with button 1 (left), since Windows only allows window
4047    * dragging using the left mouse button.
4048    */
4049 
4050   if (button != 1)
4051     return;
4052 
4053   impl = GDK_WIN32_SURFACE (window);
4054 
4055   if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE)
4056     gdk_win32_surface_end_move_resize_drag (window);
4057 
4058   setup_drag_move_resize_context (window, &impl->drag_move_resize_context,
4059                                   GDK_WIN32_DRAGOP_RESIZE, edge, device,
4060                                   button, x, y, timestamp);
4061 }
4062 
4063 static void
gdk_win32_toplevel_begin_move(GdkToplevel * toplevel,GdkDevice * device,int button,double x,double y,guint32 timestamp)4064 gdk_win32_toplevel_begin_move (GdkToplevel *toplevel,
4065                                GdkDevice   *device,
4066                                int          button,
4067                                double       x,
4068                                double       y,
4069                                guint32      timestamp)
4070 {
4071   GdkSurface *window = GDK_SURFACE (toplevel);
4072   GdkWin32Surface *impl;
4073 
4074   if (GDK_SURFACE_DESTROYED (window) ||
4075       IsIconic (GDK_SURFACE_HWND (window)))
4076     return;
4077 
4078   /* Tell Windows to start interactively moving the window by pretending that
4079    * the left pointer button was clicked in the titlebar. This will only work
4080    * if the button is down when this function is called, and will only work
4081    * with button 1 (left), since Windows only allows window dragging using the
4082    * left mouse button.
4083    */
4084   if (button != 1)
4085     return;
4086 
4087   impl = GDK_WIN32_SURFACE (window);
4088 
4089   if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE)
4090     gdk_win32_surface_end_move_resize_drag (window);
4091 
4092   setup_drag_move_resize_context (window, &impl->drag_move_resize_context,
4093                                   GDK_WIN32_DRAGOP_MOVE, GDK_SURFACE_EDGE_NORTH_WEST,
4094                                   device, button, x, y, timestamp);
4095 }
4096 
4097 
4098 /*
4099  * Setting window states
4100  */
4101 static void
gdk_win32_surface_minimize(GdkSurface * window)4102 gdk_win32_surface_minimize (GdkSurface *window)
4103 {
4104   HWND old_active_window;
4105 
4106   g_return_if_fail (GDK_IS_SURFACE (window));
4107 
4108   if (GDK_SURFACE_DESTROYED (window))
4109     return;
4110 
4111   GDK_NOTE (MISC, g_print ("gdk_surface_minimize: %p: %s\n",
4112 			   GDK_SURFACE_HWND (window),
4113 			   _gdk_win32_surface_state_to_string (window->state)));
4114 
4115   if (GDK_SURFACE_IS_MAPPED (window))
4116     {
4117       old_active_window = GetActiveWindow ();
4118       GtkShowWindow (window, SW_MINIMIZE);
4119       if (old_active_window != GDK_SURFACE_HWND (window))
4120 	SetActiveWindow (old_active_window);
4121     }
4122   else
4123     {
4124       gdk_synthesize_surface_state (window,
4125                                     0,
4126                                     GDK_TOPLEVEL_STATE_MINIMIZED);
4127     }
4128 }
4129 
4130 static void
gdk_win32_surface_maximize(GdkSurface * window)4131 gdk_win32_surface_maximize (GdkSurface *window)
4132 {
4133   GdkWin32Surface *impl;
4134 
4135   g_return_if_fail (GDK_IS_SURFACE (window));
4136 
4137   if (GDK_SURFACE_DESTROYED (window))
4138     return;
4139 
4140   GDK_NOTE (MISC, g_print ("gdk_surface_maximize: %p: %s\n",
4141 			   GDK_SURFACE_HWND (window),
4142 			   _gdk_win32_surface_state_to_string (window->state)));
4143 
4144   impl = GDK_WIN32_SURFACE (window);
4145 
4146   if (GDK_SURFACE_IS_MAPPED (window))
4147     GtkShowWindow (window, SW_MAXIMIZE);
4148   else
4149     gdk_synthesize_surface_state (window,
4150 				 0,
4151 				 GDK_TOPLEVEL_STATE_MAXIMIZED);
4152 }
4153 
4154 static void
gdk_win32_surface_unmaximize(GdkSurface * window)4155 gdk_win32_surface_unmaximize (GdkSurface *window)
4156 {
4157   g_return_if_fail (GDK_IS_SURFACE (window));
4158 
4159   if (GDK_SURFACE_DESTROYED (window))
4160     return;
4161 
4162   GDK_NOTE (MISC, g_print ("gdk_surface_unmaximize: %p: %s\n",
4163 			   GDK_SURFACE_HWND (window),
4164 			   _gdk_win32_surface_state_to_string (window->state)));
4165 
4166   _gdk_win32_surface_invalidate_egl_framebuffer (window);
4167 
4168   if (GDK_SURFACE_IS_MAPPED (window))
4169     GtkShowWindow (window, SW_RESTORE);
4170   else
4171     gdk_synthesize_surface_state (window,
4172 				 GDK_TOPLEVEL_STATE_MAXIMIZED,
4173 				 0);
4174 }
4175 
4176 static void
gdk_win32_surface_fullscreen(GdkSurface * window)4177 gdk_win32_surface_fullscreen (GdkSurface *window)
4178 {
4179   int x, y, width, height;
4180   FullscreenInfo *fi;
4181   HMONITOR monitor;
4182   MONITORINFO mi;
4183 
4184   g_return_if_fail (GDK_IS_SURFACE (window));
4185 
4186   fi = g_new (FullscreenInfo, 1);
4187 
4188   if (!GetWindowRect (GDK_SURFACE_HWND (window), &(fi->r)))
4189     g_free (fi);
4190   else
4191     {
4192       GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
4193 
4194       monitor = MonitorFromWindow (GDK_SURFACE_HWND (window), MONITOR_DEFAULTTONEAREST);
4195       mi.cbSize = sizeof (mi);
4196       if (monitor && GetMonitorInfo (monitor, &mi))
4197 	{
4198 	  x = mi.rcMonitor.left;
4199 	  y = mi.rcMonitor.top;
4200 	  width = mi.rcMonitor.right - x;
4201 	  height = mi.rcMonitor.bottom - y;
4202 	}
4203       else
4204 	{
4205 	  x = y = 0;
4206 	  width = GetSystemMetrics (SM_CXSCREEN);
4207 	  height = GetSystemMetrics (SM_CYSCREEN);
4208 	}
4209 
4210       /* remember for restoring */
4211       fi->hint_flags = impl->hint_flags;
4212       impl->hint_flags &= ~GDK_HINT_MAX_SIZE;
4213       g_object_set_data (G_OBJECT (window), "fullscreen-info", fi);
4214       fi->style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE);
4215 
4216       /* Send state change before configure event */
4217       gdk_synthesize_surface_state (window, 0, GDK_TOPLEVEL_STATE_FULLSCREEN);
4218 
4219       SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE,
4220                      (fi->style & ~WS_OVERLAPPEDWINDOW) | WS_POPUP);
4221 
4222       API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_TOP,
4223                 x, y, width, height,
4224                 SWP_NOCOPYBITS | SWP_SHOWWINDOW));
4225     }
4226 }
4227 
4228 static void
gdk_win32_surface_unfullscreen(GdkSurface * window)4229 gdk_win32_surface_unfullscreen (GdkSurface *window)
4230 {
4231   FullscreenInfo *fi;
4232 
4233   g_return_if_fail (GDK_IS_SURFACE (window));
4234 
4235   fi = g_object_get_data (G_OBJECT (window), "fullscreen-info");
4236   if (fi)
4237     {
4238       GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
4239 
4240       gdk_synthesize_surface_state (window, GDK_TOPLEVEL_STATE_FULLSCREEN, 0);
4241 
4242       impl->hint_flags = fi->hint_flags;
4243       SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, fi->style);
4244       _gdk_win32_surface_invalidate_egl_framebuffer (window);
4245       API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_NOTOPMOST,
4246 			       fi->r.left, fi->r.top,
4247 			       fi->r.right - fi->r.left, fi->r.bottom - fi->r.top,
4248 			       SWP_NOCOPYBITS | SWP_SHOWWINDOW));
4249 
4250       g_object_set_data (G_OBJECT (window), "fullscreen-info", NULL);
4251       g_free (fi);
4252       _gdk_win32_surface_update_style_bits (window);
4253     }
4254 }
4255 
4256 static void
gdk_win32_surface_focus(GdkSurface * window,guint32 timestamp)4257 gdk_win32_surface_focus (GdkSurface *window,
4258 			guint32    timestamp)
4259 {
4260   g_return_if_fail (GDK_IS_SURFACE (window));
4261 
4262   if (GDK_SURFACE_DESTROYED (window))
4263     return;
4264 
4265   GDK_NOTE (MISC, g_print ("gdk_surface_focus: %p: %s\n",
4266 			   GDK_SURFACE_HWND (window),
4267 			   _gdk_win32_surface_state_to_string (window->state)));
4268 
4269   if (window->state & GDK_TOPLEVEL_STATE_MAXIMIZED)
4270     GtkShowWindow (window, SW_SHOWMAXIMIZED);
4271   else if (window->state & GDK_TOPLEVEL_STATE_MINIMIZED)
4272     GtkShowWindow (window, SW_RESTORE);
4273   else if (!IsWindowVisible (GDK_SURFACE_HWND (window)))
4274     GtkShowWindow (window, SW_SHOWNORMAL);
4275   else
4276     GtkShowWindow (window, SW_SHOW);
4277 
4278   SetFocus (GDK_SURFACE_HWND (window));
4279 }
4280 
4281 GdkSurface *
gdk_win32_surface_lookup_for_display(GdkDisplay * display,HWND anid)4282 gdk_win32_surface_lookup_for_display (GdkDisplay *display,
4283                                      HWND        anid)
4284 {
4285   g_return_val_if_fail (display == gdk_display_get_default (), NULL);
4286 
4287   return (GdkSurface*) gdk_win32_handle_table_lookup (anid);
4288 }
4289 
4290 gboolean
gdk_win32_surface_is_win32(GdkSurface * window)4291 gdk_win32_surface_is_win32 (GdkSurface *window)
4292 {
4293   return GDK_IS_WIN32_SURFACE (window);
4294 }
4295 
4296 static gboolean
gdk_win32_surface_show_window_menu(GdkSurface * surface,GdkEvent * event)4297 gdk_win32_surface_show_window_menu (GdkSurface *surface,
4298                                     GdkEvent  *event)
4299 {
4300   double event_x, event_y;
4301   int x, y;
4302   GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
4303   GdkEventType event_type = gdk_event_get_event_type (event);
4304 
4305   switch ((int) event_type)
4306     {
4307     case GDK_BUTTON_PRESS:
4308     case GDK_BUTTON_RELEASE:
4309     case GDK_TOUCH_BEGIN:
4310     case GDK_TOUCH_END:
4311       break;
4312     default:
4313       return FALSE;
4314     }
4315 
4316   gdk_event_get_position (event, &event_x, &event_y);
4317   gdk_win32_surface_get_root_coords (surface, event_x, event_y, &x, &y);
4318 
4319   SendMessage (GDK_SURFACE_HWND (surface),
4320                WM_SYSMENU,
4321                0,
4322                MAKELPARAM (x * impl->surface_scale, y * impl->surface_scale));
4323 
4324   return TRUE;
4325 }
4326 
4327 HWND
gdk_win32_surface_get_impl_hwnd(GdkSurface * window)4328 gdk_win32_surface_get_impl_hwnd (GdkSurface *window)
4329 {
4330   if (GDK_IS_WIN32_SURFACE (window))
4331     return GDK_SURFACE_HWND (window);
4332   return NULL;
4333 }
4334 
4335 BOOL WINAPI
GtkShowWindow(GdkSurface * window,int cmd_show)4336 GtkShowWindow (GdkSurface *window,
4337                int        cmd_show)
4338 {
4339   cairo_t *cr;
4340   cairo_surface_t *surface;
4341   RECT window_rect;
4342   HDC hdc;
4343   POINT window_position;
4344   SIZE window_size;
4345   POINT source_point;
4346   BLENDFUNCTION blender;
4347 
4348   HWND hwnd = GDK_SURFACE_HWND (window);
4349   GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
4350 
4351   switch (cmd_show)
4352     {
4353     case SW_FORCEMINIMIZE:
4354     case SW_HIDE:
4355     case SW_MINIMIZE:
4356       break;
4357     case SW_MAXIMIZE:
4358     case SW_RESTORE:
4359     case SW_SHOW:
4360     case SW_SHOWDEFAULT:
4361     case SW_SHOWMINIMIZED:
4362     case SW_SHOWMINNOACTIVE:
4363     case SW_SHOWNA:
4364     case SW_SHOWNOACTIVATE:
4365     case SW_SHOWNORMAL:
4366       if (IsWindowVisible (hwnd))
4367         break;
4368 
4369       /* Window was hidden, will be shown. Erase it, GDK will repaint soon,
4370        * but not soon enough, so it's possible to see old content before
4371        * the next redraw, unless we erase the window first.
4372        */
4373       GetWindowRect (hwnd, &window_rect);
4374       source_point.x = source_point.y = 0;
4375 
4376       window_position.x = window_rect.left;
4377       window_position.y = window_rect.top;
4378       window_size.cx = window_rect.right - window_rect.left;
4379       window_size.cy = window_rect.bottom - window_rect.top;
4380 
4381       blender.BlendOp = AC_SRC_OVER;
4382       blender.BlendFlags = 0;
4383       blender.AlphaFormat = AC_SRC_ALPHA;
4384       blender.SourceConstantAlpha = 255;
4385 
4386       /* Create a surface of appropriate size and clear it */
4387       surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32,
4388                                                      window_size.cx,
4389                                                      window_size.cy);
4390       cairo_surface_set_device_scale (surface, impl->surface_scale, impl->surface_scale);
4391       cr = cairo_create (surface);
4392       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
4393       cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
4394       cairo_paint (cr);
4395       cairo_destroy (cr);
4396       cairo_surface_flush (surface);
4397       hdc = cairo_win32_surface_get_dc (surface);
4398 
4399       /* No API_CALL() wrapper, don't check for errors */
4400       UpdateLayeredWindow (hwnd, NULL,
4401                            &window_position, &window_size,
4402                            hdc, &source_point,
4403                            0, &blender, ULW_ALPHA);
4404 
4405       cairo_surface_destroy (surface);
4406 
4407       break;
4408     }
4409 
4410   /* Ensure that maximized window size is corrected later on */
4411   if (cmd_show == SW_MAXIMIZE)
4412     impl->maximizing = TRUE;
4413 
4414   return ShowWindow (hwnd, cmd_show);
4415 }
4416 
4417 static void
gdk_win32_surface_set_shadow_width(GdkSurface * window,int left,int right,int top,int bottom)4418 gdk_win32_surface_set_shadow_width (GdkSurface *window,
4419                                    int        left,
4420                                    int        right,
4421                                    int        top,
4422                                    int        bottom)
4423 {
4424   GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
4425 
4426   if (GDK_SURFACE_DESTROYED (window))
4427     return;
4428 
4429   GDK_NOTE (MISC, g_print ("gdk_win32_surface_set_shadow_width: window %p, "
4430                            "left %d, top %d, right %d, bottom %d\n",
4431                            window, left, top, right, bottom));
4432 
4433   impl->zero_shadow = left == 0 && right == 0 && top == 0 && bottom == 0;
4434 
4435   if (impl->zero_shadow)
4436     return;
4437 
4438   impl->shadow.left = left * impl->surface_scale;;
4439   impl->shadow.right = right * impl->surface_scale;
4440   impl->shadow.top = top * impl->surface_scale;;
4441   impl->shadow.bottom = bottom * impl->surface_scale;
4442   impl->shadow_x = left + right;
4443   impl->shadow_y = top + bottom;
4444 }
4445 
4446 
4447 int
_gdk_win32_surface_get_scale_factor(GdkSurface * surface)4448 _gdk_win32_surface_get_scale_factor (GdkSurface *surface)
4449 {
4450   GdkDisplay *display;
4451   GdkWin32Surface *impl;
4452   GdkWin32Display *win32_display;
4453 
4454   if (GDK_SURFACE_DESTROYED (surface))
4455     return 1;
4456 
4457   g_return_val_if_fail (surface != NULL, 1);
4458 
4459   display = gdk_surface_get_display (surface);
4460   impl = GDK_WIN32_SURFACE (surface);
4461 
4462   win32_display = GDK_WIN32_DISPLAY (display);
4463 
4464   if (win32_display->dpi_aware_type != PROCESS_DPI_UNAWARE)
4465     {
4466       if (win32_display->has_fixed_scale)
4467         impl->surface_scale = win32_display->surface_scale;
4468       else
4469         impl->surface_scale = gdk_win32_display_get_monitor_scale_factor (win32_display,
4470                                                                           surface,
4471                                                                           NULL);
4472 
4473       return impl->surface_scale;
4474     }
4475   else
4476     {
4477       if (win32_display->has_fixed_scale)
4478         {
4479           static gsize hidpi_msg_displayed = 0;
4480 
4481           if (g_once_init_enter (&hidpi_msg_displayed))
4482             {
4483               g_message ("Note: GDK_SCALE is ignored as HiDPI awareness is disabled.");
4484               g_once_init_leave (&hidpi_msg_displayed, 1);
4485             }
4486         }
4487 
4488       /* Application is not DPI aware, don't bother */
4489       return 1;
4490     }
4491 }
4492 
4493 static void
gdk_win32_surface_set_input_region(GdkSurface * window,cairo_region_t * input_region)4494 gdk_win32_surface_set_input_region (GdkSurface     *window,
4495                                     cairo_region_t *input_region)
4496 {
4497   /* Partial input shape support is implemented by handling the
4498    * NC_NCHITTEST message
4499    */
4500 }
4501 
4502 static void
compute_toplevel_size(GdkSurface * surface,gboolean update_geometry,int * width,int * height)4503 compute_toplevel_size (GdkSurface *surface,
4504                        gboolean    update_geometry,
4505                        int        *width,
4506                        int        *height)
4507 {
4508   GdkDisplay *display = gdk_surface_get_display (surface);
4509   GdkMonitor *monitor;
4510   GdkToplevelSize size;
4511   int bounds_width, bounds_height;
4512   GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
4513 
4514   monitor = gdk_display_get_monitor_at_surface (display, surface);
4515   if (monitor)
4516     {
4517       GdkRectangle workarea;
4518 
4519       gdk_win32_monitor_get_workarea (monitor, &workarea);
4520       bounds_width = workarea.width;
4521       bounds_height = workarea.height;
4522     }
4523   else
4524     {
4525       bounds_width = G_MAXINT;
4526       bounds_height = G_MAXINT;
4527     }
4528 
4529   gdk_toplevel_size_init (&size, bounds_width, bounds_height);
4530   gdk_toplevel_notify_compute_size (GDK_TOPLEVEL (surface), &size);
4531   g_warn_if_fail (size.width > 0);
4532   g_warn_if_fail (size.height > 0);
4533   *width = size.width;
4534   *height = size.height;
4535 
4536   if (size.shadow.is_valid)
4537     {
4538       gdk_win32_surface_set_shadow_width (surface,
4539                                           size.shadow.left,
4540                                           size.shadow.right,
4541                                           size.shadow.top,
4542                                           size.shadow.bottom);
4543     }
4544 
4545   if (update_geometry)
4546     {
4547       GdkGeometry geometry;
4548       GdkSurfaceHints mask;
4549       GdkToplevelLayout *layout = impl->toplevel_layout;
4550 
4551       if (gdk_toplevel_layout_get_resizable (layout))
4552         {
4553           geometry.min_width = size.min_width;
4554           geometry.min_height = size.min_height;
4555           mask = GDK_HINT_MIN_SIZE;
4556         }
4557       else
4558         {
4559           geometry.max_width = geometry.min_width = *width;
4560           geometry.max_height = geometry.min_height = *height;
4561           mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
4562         }
4563       gdk_win32_surface_set_geometry_hints (surface, &geometry, mask);
4564       gdk_surface_constrain_size (&geometry, mask, *width, *height, width, height);
4565     }
4566 }
4567 
4568 static void
_gdk_win32_surface_request_layout(GdkSurface * surface)4569 _gdk_win32_surface_request_layout (GdkSurface *surface)
4570 {
4571   GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
4572   int scale = impl->surface_scale;
4573   RECT rect;
4574 
4575   if (impl->drag_move_resize_context.native_move_resize_pending)
4576     {
4577       surface->width = impl->next_layout.configured_width;
4578       surface->height = impl->next_layout.configured_height;
4579     }
4580   else
4581     {
4582       _gdk_win32_get_window_rect (surface, &rect);
4583 
4584       impl->next_layout.configured_width = (rect.right - rect.left + scale - 1) / scale;
4585       impl->next_layout.configured_height = (rect.bottom - rect.top + scale - 1) / scale;
4586 
4587       if (GDK_IS_TOPLEVEL (surface))
4588         {
4589           surface->x = rect.left / scale;
4590           surface->y = rect.top / scale;
4591         }
4592       else if (GDK_IS_POPUP (surface))
4593         {
4594           gdk_win32_surface_get_geometry (surface,
4595                                          &surface->x, &surface->y,
4596                                           NULL, NULL);
4597         }
4598     }
4599 }
4600 
4601 static gboolean
_gdk_win32_surface_compute_size(GdkSurface * surface)4602 _gdk_win32_surface_compute_size (GdkSurface *surface)
4603 {
4604   GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
4605   int width, height;
4606 
4607   if (GDK_IS_TOPLEVEL (surface))
4608     compute_toplevel_size (surface, TRUE, &width, &height);
4609 
4610   if (!impl->drag_move_resize_context.native_move_resize_pending)
4611     {
4612       surface->width = impl->next_layout.configured_width;
4613       surface->height = impl->next_layout.configured_height;
4614 
4615       _gdk_surface_update_size (surface);
4616     }
4617 
4618   return FALSE;
4619 }
4620 
4621 static void
gdk_win32_surface_class_init(GdkWin32SurfaceClass * klass)4622 gdk_win32_surface_class_init (GdkWin32SurfaceClass *klass)
4623 {
4624   GObjectClass *object_class = G_OBJECT_CLASS (klass);
4625   GdkSurfaceClass *impl_class = GDK_SURFACE_CLASS (klass);
4626 
4627   parent_class = g_type_class_peek_parent (klass);
4628 
4629   object_class->dispose = gdk_surface_win32_dispose;
4630   object_class->finalize = gdk_surface_win32_finalize;
4631 
4632   impl_class->hide = gdk_win32_surface_hide;
4633   impl_class->get_geometry = gdk_win32_surface_get_geometry;
4634   impl_class->get_device_state = gdk_surface_win32_get_device_state;
4635   impl_class->get_root_coords = gdk_win32_surface_get_root_coords;
4636 
4637   impl_class->set_input_region = gdk_win32_surface_set_input_region;
4638   impl_class->destroy = gdk_win32_surface_destroy;
4639 
4640   //impl_class->beep = gdk_x11_surface_beep;
4641 
4642   impl_class->destroy_notify = gdk_win32_surface_destroy_notify;
4643   impl_class->drag_begin = _gdk_win32_surface_drag_begin;
4644   impl_class->get_scale_factor = _gdk_win32_surface_get_scale_factor;
4645   impl_class->request_layout = _gdk_win32_surface_request_layout;
4646   impl_class->compute_size = _gdk_win32_surface_compute_size;
4647 }
4648 
4649 HGDIOBJ
gdk_win32_surface_get_handle(GdkSurface * window)4650 gdk_win32_surface_get_handle (GdkSurface *window)
4651 {
4652   if (!GDK_IS_WIN32_SURFACE (window))
4653     {
4654       g_warning (G_STRLOC " window is not a native Win32 window");
4655       return NULL;
4656     }
4657 
4658   return GDK_SURFACE_HWND (window);
4659 }
4660 
4661 #define LAST_PROP 1
4662 
4663 typedef struct
4664 {
4665   GdkWin32Surface parent_instance;
4666 } GdkWin32Popup;
4667 
4668 typedef struct
4669 {
4670   GdkWin32SurfaceClass parent_class;
4671 } GdkWin32PopupClass;
4672 
4673 static void gdk_win32_popup_iface_init (GdkPopupInterface *iface);
4674 
G_DEFINE_TYPE_WITH_CODE(GdkWin32Popup,gdk_win32_popup,GDK_TYPE_WIN32_SURFACE,G_IMPLEMENT_INTERFACE (GDK_TYPE_POPUP,gdk_win32_popup_iface_init))4675 G_DEFINE_TYPE_WITH_CODE (GdkWin32Popup, gdk_win32_popup, GDK_TYPE_WIN32_SURFACE,
4676                          G_IMPLEMENT_INTERFACE (GDK_TYPE_POPUP,
4677                                                 gdk_win32_popup_iface_init))
4678 
4679 static void
4680 gdk_win32_popup_init (GdkWin32Popup *popup)
4681 {
4682 }
4683 
4684 static void
gdk_win32_popup_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)4685 gdk_win32_popup_get_property (GObject    *object,
4686                               guint       prop_id,
4687                               GValue     *value,
4688                               GParamSpec *pspec)
4689 {
4690   GdkSurface *surface = GDK_SURFACE (object);
4691 
4692   switch (prop_id)
4693     {
4694     case LAST_PROP + GDK_POPUP_PROP_PARENT:
4695       g_value_set_object (value, surface->parent);
4696       break;
4697 
4698     case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE:
4699       g_value_set_boolean (value, surface->autohide);
4700       break;
4701 
4702     default:
4703       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4704       break;
4705     }
4706 }
4707 
4708 static void
gdk_win32_popup_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)4709 gdk_win32_popup_set_property (GObject      *object,
4710                               guint         prop_id,
4711                               const GValue *value,
4712                               GParamSpec   *pspec)
4713 {
4714   GdkSurface *surface = GDK_SURFACE (object);
4715 
4716   switch (prop_id)
4717     {
4718     case LAST_PROP + GDK_POPUP_PROP_PARENT:
4719       surface->parent = g_value_dup_object (value);
4720       if (surface->parent != NULL)
4721         surface->parent->children = g_list_prepend (surface->parent->children, surface);
4722       break;
4723 
4724     case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE:
4725       surface->autohide = g_value_get_boolean (value);
4726       break;
4727 
4728     default:
4729       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4730       break;
4731     }
4732 }
4733 
4734 static void
gdk_win32_popup_class_init(GdkWin32PopupClass * class)4735 gdk_win32_popup_class_init (GdkWin32PopupClass *class)
4736 {
4737   GObjectClass *object_class = G_OBJECT_CLASS (class);
4738 
4739   object_class->get_property = gdk_win32_popup_get_property;
4740   object_class->set_property = gdk_win32_popup_set_property;
4741 
4742   gdk_popup_install_properties (object_class, 1);
4743 }
4744 
4745 static gboolean
gdk_win32_popup_present(GdkPopup * popup,int width,int height,GdkPopupLayout * layout)4746 gdk_win32_popup_present (GdkPopup       *popup,
4747                          int             width,
4748                          int             height,
4749                          GdkPopupLayout *layout)
4750 {
4751   return gdk_win32_surface_present_popup (GDK_SURFACE (popup), width, height, layout);
4752 }
4753 
4754 static GdkGravity
gdk_win32_popup_get_surface_anchor(GdkPopup * popup)4755 gdk_win32_popup_get_surface_anchor (GdkPopup *popup)
4756 {
4757   return GDK_SURFACE (popup)->popup.surface_anchor;
4758 }
4759 
4760 static GdkGravity
gdk_win32_popup_get_rect_anchor(GdkPopup * popup)4761 gdk_win32_popup_get_rect_anchor (GdkPopup *popup)
4762 {
4763   return GDK_SURFACE (popup)->popup.rect_anchor;
4764 }
4765 
4766 static int
gdk_win32_popup_get_position_x(GdkPopup * popup)4767 gdk_win32_popup_get_position_x (GdkPopup *popup)
4768 {
4769   return GDK_SURFACE (popup)->x;
4770 }
4771 
4772 static int
gdk_win32_popup_get_position_y(GdkPopup * popup)4773 gdk_win32_popup_get_position_y (GdkPopup *popup)
4774 {
4775   return GDK_SURFACE (popup)->y;
4776 }
4777 
4778 static void
gdk_win32_popup_iface_init(GdkPopupInterface * iface)4779 gdk_win32_popup_iface_init (GdkPopupInterface *iface)
4780 {
4781   iface->present = gdk_win32_popup_present;
4782   iface->get_surface_anchor = gdk_win32_popup_get_surface_anchor;
4783   iface->get_rect_anchor = gdk_win32_popup_get_rect_anchor;
4784   iface->get_position_x = gdk_win32_popup_get_position_x;
4785   iface->get_position_y = gdk_win32_popup_get_position_y;
4786 }
4787 
4788 
4789 typedef struct
4790 {
4791   GdkWin32Surface parent_instance;
4792 } GdkWin32Toplevel;
4793 
4794 typedef struct
4795 {
4796   GdkWin32SurfaceClass parent_class;
4797 } GdkWin32ToplevelClass;
4798 
4799 static void gdk_win32_toplevel_iface_init (GdkToplevelInterface *iface);
4800 
G_DEFINE_TYPE_WITH_CODE(GdkWin32Toplevel,gdk_win32_toplevel,GDK_TYPE_WIN32_SURFACE,G_IMPLEMENT_INTERFACE (GDK_TYPE_TOPLEVEL,gdk_win32_toplevel_iface_init))4801 G_DEFINE_TYPE_WITH_CODE (GdkWin32Toplevel, gdk_win32_toplevel, GDK_TYPE_WIN32_SURFACE,
4802                          G_IMPLEMENT_INTERFACE (GDK_TYPE_TOPLEVEL,
4803                                                 gdk_win32_toplevel_iface_init))
4804 
4805 static void
4806 gdk_win32_toplevel_init (GdkWin32Toplevel *toplevel)
4807 {
4808 }
4809 
4810 static void
gdk_win32_toplevel_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)4811 gdk_win32_toplevel_set_property (GObject      *object,
4812                                    guint         prop_id,
4813                                    const GValue *value,
4814                                    GParamSpec   *pspec)
4815 {
4816   GdkSurface *surface = GDK_SURFACE (object);
4817 
4818   switch (prop_id)
4819     {
4820     case LAST_PROP + GDK_TOPLEVEL_PROP_TITLE:
4821       gdk_win32_surface_set_title (surface, g_value_get_string (value));
4822       g_object_notify_by_pspec (G_OBJECT (surface), pspec);
4823       break;
4824 
4825     case LAST_PROP + GDK_TOPLEVEL_PROP_STARTUP_ID:
4826       break;
4827 
4828     case LAST_PROP + GDK_TOPLEVEL_PROP_TRANSIENT_FOR:
4829       gdk_win32_surface_set_transient_for (surface, g_value_get_object (value));
4830       g_object_notify_by_pspec (G_OBJECT (surface), pspec);
4831       break;
4832 
4833     case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL:
4834       GDK_SURFACE (surface)->modal_hint = g_value_get_boolean (value);
4835 
4836       if (GDK_SURFACE (surface)->modal_hint)
4837         {
4838           SetCapture (GDK_SURFACE_HWND (surface));
4839           _gdk_push_modal_window (surface);
4840         }
4841 
4842       g_object_notify_by_pspec (G_OBJECT (surface), pspec);
4843       break;
4844 
4845     case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST:
4846       break;
4847 
4848     case LAST_PROP + GDK_TOPLEVEL_PROP_DECORATED:
4849       GDK_WIN32_SURFACE (surface)->decorate_all = g_value_get_boolean (value);
4850       _gdk_win32_surface_update_style_bits (surface);
4851       g_object_notify_by_pspec (G_OBJECT (surface), pspec);
4852       break;
4853 
4854     case LAST_PROP + GDK_TOPLEVEL_PROP_DELETABLE:
4855       break;
4856 
4857     case LAST_PROP + GDK_TOPLEVEL_PROP_FULLSCREEN_MODE:
4858       surface->fullscreen_mode = g_value_get_enum (value);
4859       g_object_notify_by_pspec (G_OBJECT (surface), pspec);
4860       break;
4861 
4862     case LAST_PROP + GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED:
4863       break;
4864 
4865     default:
4866       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4867       break;
4868     }
4869 }
4870 
4871 static void
gdk_win32_toplevel_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)4872 gdk_win32_toplevel_get_property (GObject    *object,
4873                                    guint       prop_id,
4874                                    GValue     *value,
4875                                    GParamSpec *pspec)
4876 {
4877   GdkSurface *surface = GDK_SURFACE (object);
4878 
4879   switch (prop_id)
4880     {
4881     case LAST_PROP + GDK_TOPLEVEL_PROP_STATE:
4882       g_value_set_flags (value, surface->state);
4883       break;
4884 
4885     case LAST_PROP + GDK_TOPLEVEL_PROP_TITLE:
4886       break;
4887 
4888     case LAST_PROP + GDK_TOPLEVEL_PROP_STARTUP_ID:
4889       break;
4890 
4891     case LAST_PROP + GDK_TOPLEVEL_PROP_TRANSIENT_FOR:
4892       g_value_set_object (value, surface->transient_for);
4893       break;
4894 
4895     case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL:
4896       g_value_set_boolean (value, GDK_SURFACE (surface)->modal_hint);
4897       break;
4898 
4899     case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST:
4900       g_value_set_pointer (value, NULL);
4901       break;
4902 
4903     case LAST_PROP + GDK_TOPLEVEL_PROP_DECORATED:
4904       g_value_set_boolean (value, GDK_WIN32_SURFACE (surface)->decorate_all);
4905       break;
4906 
4907     case LAST_PROP + GDK_TOPLEVEL_PROP_DELETABLE:
4908       break;
4909 
4910     case LAST_PROP + GDK_TOPLEVEL_PROP_FULLSCREEN_MODE:
4911       g_value_set_enum (value, surface->fullscreen_mode);
4912       break;
4913 
4914     case LAST_PROP + GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED:
4915       g_value_set_boolean (value, surface->shortcuts_inhibited);
4916       break;
4917 
4918     default:
4919       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4920       break;
4921     }
4922 }
4923 
4924 static void
gdk_win32_toplevel_class_init(GdkWin32ToplevelClass * class)4925 gdk_win32_toplevel_class_init (GdkWin32ToplevelClass *class)
4926 {
4927   GObjectClass *object_class = G_OBJECT_CLASS (class);
4928 
4929   object_class->get_property = gdk_win32_toplevel_get_property;
4930   object_class->set_property = gdk_win32_toplevel_set_property;
4931 
4932   gdk_toplevel_install_properties (object_class, 1);
4933 }
4934 
4935 static void
gdk_win32_toplevel_present(GdkToplevel * toplevel,GdkToplevelLayout * layout)4936 gdk_win32_toplevel_present (GdkToplevel       *toplevel,
4937                             GdkToplevelLayout *layout)
4938 {
4939   GdkSurface *surface = GDK_SURFACE (toplevel);
4940   GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
4941   int width, height;
4942   gboolean maximize;
4943   gboolean fullscreen;
4944 
4945   g_clear_pointer (&impl->toplevel_layout, gdk_toplevel_layout_unref);
4946   impl->toplevel_layout = gdk_toplevel_layout_copy (layout);
4947   compute_toplevel_size (surface, FALSE, &width, &height);
4948   gdk_win32_surface_resize (surface, width, height);
4949 
4950   if (gdk_toplevel_layout_get_maximized (layout, &maximize))
4951     {
4952       if (maximize)
4953         gdk_win32_surface_maximize (surface);
4954       else
4955         gdk_win32_surface_unmaximize (surface);
4956     }
4957 
4958   if (gdk_toplevel_layout_get_fullscreen (layout, &fullscreen))
4959     {
4960       if (fullscreen)
4961         gdk_win32_surface_fullscreen (surface);
4962       else
4963         gdk_win32_surface_unfullscreen (surface);
4964     }
4965 
4966   gdk_win32_surface_show (surface, FALSE);
4967   maybe_notify_mapped (surface);
4968 }
4969 
4970 static gboolean
gdk_win32_toplevel_minimize(GdkToplevel * toplevel)4971 gdk_win32_toplevel_minimize (GdkToplevel *toplevel)
4972 {
4973   gdk_win32_surface_minimize (GDK_SURFACE (toplevel));
4974 
4975   return TRUE;
4976 }
4977 
4978 static gboolean
gdk_win32_toplevel_lower(GdkToplevel * toplevel)4979 gdk_win32_toplevel_lower (GdkToplevel *toplevel)
4980 {
4981   return FALSE;
4982 }
4983 
4984 static void
gdk_win32_toplevel_focus(GdkToplevel * toplevel,guint32 timestamp)4985 gdk_win32_toplevel_focus (GdkToplevel *toplevel,
4986                             guint32      timestamp)
4987 {
4988   gdk_win32_surface_focus (GDK_SURFACE (toplevel), timestamp);
4989 }
4990 
4991 static gboolean
gdk_win32_toplevel_show_window_menu(GdkToplevel * toplevel,GdkEvent * event)4992 gdk_win32_toplevel_show_window_menu (GdkToplevel *toplevel,
4993                                        GdkEvent    *event)
4994 {
4995   return gdk_win32_surface_show_window_menu (GDK_SURFACE (toplevel), event);
4996 }
4997 
4998 static gboolean
gdk_win32_toplevel_supports_edge_constraints(GdkToplevel * toplevel)4999 gdk_win32_toplevel_supports_edge_constraints (GdkToplevel *toplevel)
5000 {
5001   return FALSE;
5002 }
5003 
5004 static void
gdk_win32_toplevel_iface_init(GdkToplevelInterface * iface)5005 gdk_win32_toplevel_iface_init (GdkToplevelInterface *iface)
5006 {
5007   iface->present = gdk_win32_toplevel_present;
5008   iface->minimize = gdk_win32_toplevel_minimize;
5009   iface->lower = gdk_win32_toplevel_lower;
5010   iface->focus = gdk_win32_toplevel_focus;
5011   iface->show_window_menu = gdk_win32_toplevel_show_window_menu;
5012   iface->supports_edge_constraints = gdk_win32_toplevel_supports_edge_constraints;
5013   iface->begin_resize = gdk_win32_toplevel_begin_resize;
5014   iface->begin_move = gdk_win32_toplevel_begin_move;
5015 }
5016 
5017 typedef struct
5018 {
5019   GdkWin32Surface parent_instance;
5020 } GdkWin32DragSurface;
5021 
5022 typedef struct
5023 {
5024   GdkWin32SurfaceClass parent_class;
5025 } GdkWin32DragSurfaceClass;
5026 
5027 static void gdk_win32_drag_surface_iface_init (GdkDragSurfaceInterface *iface);
5028 
G_DEFINE_TYPE_WITH_CODE(GdkWin32DragSurface,gdk_win32_drag_surface,GDK_TYPE_WIN32_SURFACE,G_IMPLEMENT_INTERFACE (GDK_TYPE_DRAG_SURFACE,gdk_win32_drag_surface_iface_init))5029 G_DEFINE_TYPE_WITH_CODE (GdkWin32DragSurface, gdk_win32_drag_surface, GDK_TYPE_WIN32_SURFACE,
5030                          G_IMPLEMENT_INTERFACE (GDK_TYPE_DRAG_SURFACE,
5031                                                 gdk_win32_drag_surface_iface_init))
5032 
5033 static void
5034 gdk_win32_drag_surface_init (GdkWin32DragSurface *surface)
5035 {
5036 }
5037 
5038 static void
gdk_win32_drag_surface_class_init(GdkWin32DragSurfaceClass * class)5039 gdk_win32_drag_surface_class_init (GdkWin32DragSurfaceClass *class)
5040 {
5041 }
5042 
5043 static gboolean
gdk_win32_drag_surface_present(GdkDragSurface * drag_surface,int width,int height)5044 gdk_win32_drag_surface_present (GdkDragSurface *drag_surface,
5045                                   int             width,
5046                                   int             height)
5047 {
5048   GdkSurface *surface = GDK_SURFACE (drag_surface);
5049 
5050   gdk_win32_surface_resize (surface, width, height);
5051   gdk_win32_surface_show (surface, FALSE);
5052   maybe_notify_mapped (surface);
5053 
5054   return TRUE;
5055 }
5056 
5057 static void
gdk_win32_drag_surface_iface_init(GdkDragSurfaceInterface * iface)5058 gdk_win32_drag_surface_iface_init (GdkDragSurfaceInterface *iface)
5059 {
5060   iface->present = gdk_win32_drag_surface_present;
5061 }
5062 
5063 #ifdef GDK_WIN32_ENABLE_EGL
5064 EGLSurface
gdk_win32_surface_get_egl_surface(GdkSurface * surface,EGLConfig config,gboolean is_dummy)5065 gdk_win32_surface_get_egl_surface (GdkSurface *surface,
5066                                    EGLConfig   config,
5067                                    gboolean    is_dummy)
5068 {
5069   GdkWin32Display *display = GDK_WIN32_DISPLAY (gdk_surface_get_display (surface));
5070   GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
5071 
5072   if (is_dummy)
5073     {
5074       if (impl->egl_dummy_surface == EGL_NO_SURFACE)
5075         {
5076           EGLint attribs[] = {EGL_WIDTH, 1, EGL_WIDTH, 1, EGL_NONE};
5077           impl->egl_dummy_surface = eglCreatePbufferSurface (display->egl_disp,
5078                                                              config,
5079                                                              attribs);
5080         }
5081       return impl->egl_dummy_surface;
5082     }
5083   else
5084     {
5085       if (impl->egl_surface == EGL_NO_SURFACE)
5086         impl->egl_surface = eglCreateWindowSurface (display->egl_disp,
5087                                                     config,
5088                                                     GDK_SURFACE_HWND (surface),
5089                                                     NULL);
5090 
5091       return impl->egl_surface;
5092     }
5093 
5094 }
5095 #endif
5096 
5097 static void
gdk_win32_surface_get_queued_window_rect(GdkSurface * surface,int scale,RECT * return_window_rect)5098 gdk_win32_surface_get_queued_window_rect (GdkSurface *surface,
5099                                           int         scale,
5100                                           RECT       *return_window_rect)
5101 {
5102   RECT window_rect;
5103   GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
5104 
5105   _gdk_win32_get_window_client_area_rect (surface, scale, &window_rect);
5106 
5107   /* Turn client area into window area */
5108   _gdk_win32_adjust_client_rect (surface, &window_rect);
5109 
5110   /* Convert GDK screen coordinates to W32 desktop coordinates */
5111   window_rect.left -= _gdk_offset_x * impl->surface_scale;
5112   window_rect.right -= _gdk_offset_x * impl->surface_scale;
5113   window_rect.top -= _gdk_offset_y * impl->surface_scale;
5114   window_rect.bottom -= _gdk_offset_y * impl->surface_scale;
5115 
5116   *return_window_rect = window_rect;
5117 }
5118 
5119 static void
gdk_win32_surface_apply_queued_move_resize(GdkSurface * surface,RECT window_rect)5120 gdk_win32_surface_apply_queued_move_resize (GdkSurface *surface,
5121                                             RECT        window_rect)
5122 {
5123   if (!IsIconic (GDK_SURFACE_HWND (surface)))
5124     {
5125       GDK_NOTE (EVENTS, g_print ("Setting window position ... "));
5126 
5127       API_CALL (SetWindowPos, (GDK_SURFACE_HWND (surface),
5128                                SWP_NOZORDER_SPECIFIED,
5129                                window_rect.left, window_rect.top,
5130                                window_rect.right - window_rect.left,
5131                                window_rect.bottom - window_rect.top,
5132                                SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW));
5133 
5134       GDK_NOTE (EVENTS, g_print (" ... set window position\n"));
5135 
5136       return;
5137     }
5138 
5139   /* Don't move iconic windows */
5140   /* TODO: use SetWindowPlacement() to change non-minimized window position */
5141 }
5142 
5143 RECT
gdk_win32_surface_handle_queued_move_resize(GdkDrawContext * draw_context)5144 gdk_win32_surface_handle_queued_move_resize (GdkDrawContext *draw_context)
5145 {
5146   GdkSurface *surface;
5147   GdkWin32Surface *impl;
5148   int scale;
5149   RECT queued_window_rect;
5150 
5151   surface = gdk_draw_context_get_surface (draw_context);
5152   impl = GDK_WIN32_SURFACE (surface);
5153   scale = gdk_surface_get_scale_factor (surface);
5154 
5155   gdk_win32_surface_get_queued_window_rect (surface, scale, &queued_window_rect);
5156 
5157   /* Apply queued resizes for non-double-buffered windows
5158    * before painting them (we paint on the window DC directly,
5159    * it must have the right size).
5160    * Due to some poorly-undetstood issue delayed
5161    * resizing of double-buffered windows can produce weird
5162    * artefacts, so these are also resized before we paint.
5163    */
5164   if (impl->drag_move_resize_context.native_move_resize_pending)
5165     {
5166       impl->drag_move_resize_context.native_move_resize_pending = FALSE;
5167       gdk_win32_surface_apply_queued_move_resize (surface, queued_window_rect);
5168     }
5169 
5170   return queued_window_rect;
5171 }
5172 
5173 void
_gdk_win32_surface_invalidate_egl_framebuffer(GdkSurface * surface)5174 _gdk_win32_surface_invalidate_egl_framebuffer (GdkSurface *surface)
5175 {
5176 /* If we are using ANGLE, we need to force redraw of the whole Window and its child windows
5177  *  as we need to re-acquire the EGL surfaces that we rendered to upload to Cairo explicitly,
5178  *  using gdk_window_invalidate_rect (), when we maximize or restore or use aerosnap
5179  */
5180 #ifdef GDK_WIN32_ENABLE_EGL
5181   if (surface->gl_paint_context != NULL && gdk_gl_context_get_use_es (surface->gl_paint_context))
5182     {
5183       GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
5184 
5185       impl->egl_force_redraw_all = TRUE;
5186     }
5187 #endif
5188 }
5189