1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/toplevel.cpp
3 // Purpose:
4 // Author:      Robert Roebling
5 // Copyright:   (c) 1998 Robert Roebling
6 // Licence:     wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8 
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11 
12 // ============================================================================
13 // declarations
14 // ============================================================================
15 
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19 
20 #ifdef __VMS
21 #define XIconifyWindow XICONIFYWINDOW
22 #endif
23 
24 #include "wx/toplevel.h"
25 
26 #ifndef WX_PRECOMP
27     #include "wx/frame.h"
28     #include "wx/app.h"     // GetAppDisplayName()
29     #include "wx/icon.h"
30     #include "wx/log.h"
31 #endif
32 
33 #include "wx/evtloop.h"
34 #include "wx/sysopt.h"
35 
36 #include "wx/gtk/private.h"
37 #include "wx/gtk/private/gtk3-compat.h"
38 #include "wx/gtk/private/stylecontext.h"
39 #include "wx/gtk/private/win_gtk.h"
40 
41 #ifdef GDK_WINDOWING_X11
42     #include <gdk/gdkx.h>
43     #include <X11/Xatom.h>  // XA_CARDINAL
44     #include "wx/unix/utilsx11.h"
45 #endif
46 
47 // ----------------------------------------------------------------------------
48 // data
49 // ----------------------------------------------------------------------------
50 
51 // this is incremented while a modal dialog is shown
52 int wxOpenModalDialogsCount = 0;
53 
54 // the frame that is currently active (i.e. its child has focus). It is
55 // used to generate wxActivateEvents
56 static wxTopLevelWindowGTK *g_activeFrame = NULL;
57 
58 extern wxCursor g_globalCursor;
59 extern wxCursor g_busyCursor;
60 extern bool g_inSizeAllocate;
61 
62 #ifdef GDK_WINDOWING_X11
63 // Whether _NET_REQUEST_FRAME_EXTENTS support is working
64 static enum {
65     RFE_STATUS_UNKNOWN, RFE_STATUS_WORKING, RFE_STATUS_BROKEN
66 } gs_requestFrameExtentsStatus;
67 
68 static bool gs_decorCacheValid;
69 #endif
70 
71 #ifdef __WXGTK3__
HasClientDecor(GtkWidget * widget)72 static bool HasClientDecor(GtkWidget* widget)
73 {
74     static bool has;
75     static bool once;
76     if (!once)
77     {
78         once = true;
79         GdkDisplay* display = gtk_widget_get_display(widget);
80         const char* name = g_type_name(G_TYPE_FROM_INSTANCE(display));
81         has =
82             strcmp(name, "GdkWaylandDisplay") == 0 ||
83             strcmp(name, "GdkMirDisplay") == 0 ||
84             strcmp(name, "GdkBroadwayDisplay") == 0;
85     }
86     return has;
87 }
88 #else
HasClientDecor(GtkWidget *)89 static inline bool HasClientDecor(GtkWidget*)
90 {
91     return false;
92 }
93 #endif
94 
95 //-----------------------------------------------------------------------------
96 // RequestUserAttention related functions
97 //-----------------------------------------------------------------------------
98 
99 #ifndef __WXGTK3__
wxgtk_window_set_urgency_hint(GtkWindow * win,gboolean setting)100 static void wxgtk_window_set_urgency_hint (GtkWindow *win,
101                                            gboolean setting)
102 {
103 #if GTK_CHECK_VERSION(2,7,0)
104     if (wx_is_at_least_gtk2(7))
105         gtk_window_set_urgency_hint(win, setting);
106     else
107 #endif
108     {
109 #ifdef GDK_WINDOWING_X11
110         GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(win));
111         wxCHECK_RET(window, "wxgtk_window_set_urgency_hint: GdkWindow not realized");
112 
113         Display* dpy = GDK_WINDOW_XDISPLAY(window);
114         Window xid = GDK_WINDOW_XID(window);
115         XWMHints* wm_hints = XGetWMHints(dpy, xid);
116 
117         if (!wm_hints)
118             wm_hints = XAllocWMHints();
119 
120         if (setting)
121             wm_hints->flags |= XUrgencyHint;
122         else
123             wm_hints->flags &= ~XUrgencyHint;
124 
125         XSetWMHints(dpy, xid, wm_hints);
126         XFree(wm_hints);
127 #endif // GDK_WINDOWING_X11
128     }
129 }
130 #define gtk_window_set_urgency_hint wxgtk_window_set_urgency_hint
131 #endif
132 
133 extern "C" {
gtk_frame_urgency_timer_callback(wxTopLevelWindowGTK * win)134 static gboolean gtk_frame_urgency_timer_callback( wxTopLevelWindowGTK *win )
135 {
136     gtk_window_set_urgency_hint(GTK_WINDOW(win->m_widget), false);
137 
138     win->m_urgency_hint = -2;
139     return FALSE;
140 }
141 }
142 
143 //-----------------------------------------------------------------------------
144 // "focus_in_event"
145 //-----------------------------------------------------------------------------
146 
147 extern "C" {
gtk_frame_focus_in_callback(GtkWidget * widget,GdkEvent * WXUNUSED (event),wxTopLevelWindowGTK * win)148 static gboolean gtk_frame_focus_in_callback( GtkWidget *widget,
149                                          GdkEvent *WXUNUSED(event),
150                                          wxTopLevelWindowGTK *win )
151 {
152     g_activeFrame = win;
153 
154     // MR: wxRequestUserAttention related block
155     switch( win->m_urgency_hint )
156     {
157         default:
158             g_source_remove( win->m_urgency_hint );
159             // no break, fallthrough to remove hint too
160             wxFALLTHROUGH;
161         case -1:
162             gtk_window_set_urgency_hint(GTK_WINDOW(widget), false);
163             win->m_urgency_hint = -2;
164             break;
165 
166         case -2: break;
167     }
168 
169     wxActivateEvent event(wxEVT_ACTIVATE, true, g_activeFrame->GetId());
170     event.SetEventObject(g_activeFrame);
171     g_activeFrame->HandleWindowEvent(event);
172 
173     return FALSE;
174 }
175 }
176 
177 //-----------------------------------------------------------------------------
178 // "focus_out_event"
179 //-----------------------------------------------------------------------------
180 
181 extern "C" {
182 static
gtk_frame_focus_out_callback(GtkWidget * WXUNUSED (widget),GdkEventFocus * WXUNUSED (gdk_event),wxTopLevelWindowGTK * WXUNUSED (win))183 gboolean gtk_frame_focus_out_callback(GtkWidget * WXUNUSED(widget),
184                                       GdkEventFocus *WXUNUSED(gdk_event),
185                                       wxTopLevelWindowGTK * WXUNUSED(win))
186 {
187     if (g_activeFrame)
188     {
189         wxActivateEvent event(wxEVT_ACTIVATE, false, g_activeFrame->GetId());
190         event.SetEventObject(g_activeFrame);
191         g_activeFrame->HandleWindowEvent(event);
192 
193         g_activeFrame = NULL;
194     }
195 
196     return FALSE;
197 }
198 }
199 
200 // ----------------------------------------------------------------------------
201 // "key_press_event"
202 // ----------------------------------------------------------------------------
203 
204 extern "C" {
205 static gboolean
wxgtk_tlw_key_press_event(GtkWidget * widget,GdkEventKey * event)206 wxgtk_tlw_key_press_event(GtkWidget *widget, GdkEventKey *event)
207 {
208     GtkWindow* const window = GTK_WINDOW(widget);
209 
210     // By default GTK+ checks for the menu accelerators in this (top level)
211     // window first and then propagates the event to the currently focused
212     // child from where it bubbles up the window parent chain. In wxWidgets,
213     // however, we want the child window to have the event first but still
214     // handle it as an accelerator if it's not processed there, so we need to
215     // customize this by reversing the order of the steps done in the standard
216     // GTK+ gtk_window_key_press_event() handler.
217 
218     if ( gtk_window_propagate_key_event(window, event) )
219         return true;
220 
221     if ( gtk_window_activate_key(window, event) )
222         return true;
223 
224     void* parent_class = g_type_class_peek_parent(G_OBJECT_GET_CLASS(widget));
225     GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
226 
227     // Avoid calling the default handler, we have already done everything it does
228     return true;
229 }
230 }
231 
232 //-----------------------------------------------------------------------------
233 // "size_allocate" from m_wxwindow
234 //-----------------------------------------------------------------------------
235 
236 extern "C" {
237 static void
size_allocate(GtkWidget *,GtkAllocation * alloc,wxTopLevelWindowGTK * win)238 size_allocate(GtkWidget*, GtkAllocation* alloc, wxTopLevelWindowGTK* win)
239 {
240     win->m_useCachedClientSize = true;
241     if (win->m_clientWidth  != alloc->width ||
242         win->m_clientHeight != alloc->height)
243     {
244         const bool save_inSizeAllocate = g_inSizeAllocate;
245         g_inSizeAllocate = true;
246 
247         win->m_clientWidth  = alloc->width;
248         win->m_clientHeight = alloc->height;
249 
250         GtkAllocation a;
251         gtk_widget_get_allocation(win->m_widget, &a);
252         wxSize size(a.width, a.height);
253         if (HasClientDecor(win->m_widget))
254         {
255             GtkAllocation a2;
256             gtk_widget_get_allocation(win->m_mainWidget, &a2);
257             wxTopLevelWindowGTK::DecorSize decorSize;
258             decorSize.left = a2.x;
259             decorSize.right = a.width - a2.width - a2.x;
260             decorSize.top = a2.y;
261             decorSize.bottom = a.height - a2.height - a2.y;
262             win->GTKUpdateDecorSize(decorSize);
263         }
264         else
265         {
266             size.x += win->m_decorSize.left + win->m_decorSize.right;
267             size.y += win->m_decorSize.top + win->m_decorSize.bottom;
268         }
269         win->m_width  = size.x;
270         win->m_height = size.y;
271 
272         if (!win->IsIconized())
273         {
274             wxSizeEvent event(size, win->GetId());
275             event.SetEventObject(win);
276             win->HandleWindowEvent(event);
277         }
278         // else the window is currently unmapped, don't generate size events
279 
280         g_inSizeAllocate = save_inSizeAllocate;
281     }
282 }
283 }
284 
285 //-----------------------------------------------------------------------------
286 // "delete_event"
287 //-----------------------------------------------------------------------------
288 
289 extern "C" {
290 static gboolean
gtk_frame_delete_callback(GtkWidget * WXUNUSED (widget),GdkEvent * WXUNUSED (event),wxTopLevelWindowGTK * win)291 gtk_frame_delete_callback( GtkWidget *WXUNUSED(widget),
292                            GdkEvent *WXUNUSED(event),
293                            wxTopLevelWindowGTK *win )
294 {
295     if (win->IsEnabled() &&
296         (wxOpenModalDialogsCount == 0 || (win->GetExtraStyle() & wxTOPLEVEL_EX_DIALOG) ||
297          win->IsGrabbed()))
298         win->Close();
299 
300     return TRUE;
301 }
302 }
303 
304 //-----------------------------------------------------------------------------
305 // "configure_event"
306 //-----------------------------------------------------------------------------
307 
308 extern "C" {
309 static gboolean
gtk_frame_configure_callback(GtkWidget *,GdkEventConfigure * gdk_event,wxTopLevelWindowGTK * win)310 gtk_frame_configure_callback( GtkWidget*,
311                               GdkEventConfigure* gdk_event,
312                               wxTopLevelWindowGTK *win )
313 {
314     win->GTKConfigureEvent(gdk_event->x, gdk_event->y);
315     return false;
316 }
317 }
318 
GTKConfigureEvent(int x,int y)319 void wxTopLevelWindowGTK::GTKConfigureEvent(int x, int y)
320 {
321     wxPoint point;
322 #ifdef GDK_WINDOWING_X11
323     if (gs_decorCacheValid)
324     {
325         const DecorSize& decorSize = GetCachedDecorSize();
326         point.x = x - decorSize.left;
327         point.y = y - decorSize.top;
328     }
329     else
330 #endif
331     {
332         gtk_window_get_position(GTK_WINDOW(m_widget), &point.x, &point.y);
333     }
334 
335     if (m_x != point.x || m_y != point.y)
336     {
337         m_lastPos = wxPoint(m_x, m_y);
338 
339         m_x = point.x;
340         m_y = point.y;
341         wxMoveEvent event(point, GetId());
342         event.SetEventObject(this);
343         HandleWindowEvent(event);
344     }
345 }
346 
347 //-----------------------------------------------------------------------------
348 // "realize" from m_widget
349 //-----------------------------------------------------------------------------
350 
351 #if GTK_CHECK_VERSION(3,10,0)
352 extern "C" {
findTitlebar(GtkWidget * widget,void * data)353 static void findTitlebar(GtkWidget* widget, void* data)
354 {
355     if (GTK_IS_HEADER_BAR(widget))
356         *static_cast<GtkWidget**>(data) = widget;
357 }
358 }
359 #endif
360 
361 // we cannot the WM hints and icons before the widget has been realized,
362 // so we do this directly after realization
363 
GTKHandleRealized()364 void wxTopLevelWindowGTK::GTKHandleRealized()
365 {
366     wxNonOwnedWindow::GTKHandleRealized();
367 
368     GdkWindow* window = gtk_widget_get_window(m_widget);
369 
370 #if GTK_CHECK_VERSION(3,10,0)
371     if (wx_is_at_least_gtk3(10))
372     {
373         GtkWidget* titlebar = NULL;
374         gtk_container_forall(GTK_CONTAINER(m_widget), findTitlebar, &titlebar);
375         if (titlebar)
376         {
377 #if GTK_CHECK_VERSION(3,12,0)
378             if (m_gdkDecor && wx_is_at_least_gtk3(12))
379             {
380                 char layout[sizeof("icon,menu:minimize,maximize,close")];
381                 snprintf(layout, sizeof(layout), "icon%s:%s%s%s",
382                      m_gdkDecor & GDK_DECOR_MENU ? ",menu" : "",
383                      m_gdkDecor & GDK_DECOR_MINIMIZE ? "minimize," : "",
384                      m_gdkDecor & GDK_DECOR_MAXIMIZE ? "maximize," : "",
385                      m_gdkFunc & GDK_FUNC_CLOSE ? "close" : "");
386                 gtk_header_bar_set_decoration_layout(GTK_HEADER_BAR(titlebar), layout);
387             }
388 #endif // 3.12
389             // Don't set WM decorations when GTK is using Client Side Decorations
390             m_gdkDecor = 0;
391         }
392     }
393 #endif // 3.10
394 
395     gdk_window_set_decorations(window, (GdkWMDecoration)m_gdkDecor);
396     gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc);
397 
398     const wxIconBundle& icons = GetIcons();
399     if (icons.GetIconCount())
400         SetIcons(icons);
401 
402     GdkCursor* cursor = g_globalCursor.GetCursor();
403     if (wxIsBusy() && !gtk_window_get_modal(GTK_WINDOW(m_widget)))
404         cursor = g_busyCursor.GetCursor();
405 
406     if (cursor)
407         gdk_window_set_cursor(window, cursor);
408 
409 #ifdef __WXGTK3__
410     wxGCC_WARNING_SUPPRESS(deprecated-declarations)
411     if (gtk_window_get_has_resize_grip(GTK_WINDOW(m_widget)))
412     {
413         // Grip window can end up obscured, probably due to deferred show.
414         // Reset grip to ensure it is visible.
415         gtk_window_set_has_resize_grip(GTK_WINDOW(m_widget), false);
416         gtk_window_set_has_resize_grip(GTK_WINDOW(m_widget), true);
417     }
418     wxGCC_WARNING_RESTORE()
419 #endif
420 }
421 
422 //-----------------------------------------------------------------------------
423 // "map_event" from m_widget
424 //-----------------------------------------------------------------------------
425 
426 extern "C" {
427 static gboolean
gtk_frame_map_callback(GtkWidget *,GdkEvent * WXUNUSED (event),wxTopLevelWindow * win)428 gtk_frame_map_callback( GtkWidget*,
429                         GdkEvent * WXUNUSED(event),
430                         wxTopLevelWindow *win )
431 {
432     const bool wasIconized = win->IsIconized();
433     if (wasIconized)
434     {
435         // Because GetClientSize() returns (0,0) when IsIconized() is true,
436         // a size event must be generated, just in case GetClientSize() was
437         // called while iconized. This specifically happens when restoring a
438         // tlw that was "rolled up" with some WMs.
439         // Queue a resize rather than sending size event directly to allow
440         // children to be made visible first.
441         win->m_useCachedClientSize = false;
442         win->m_clientWidth = 0;
443         gtk_widget_queue_resize(win->m_wxwindow);
444     }
445     // it is possible for m_isShown to be false here, see bug #9909
446     if (win->wxWindowBase::Show(true))
447     {
448         win->GTKDoAfterShow();
449     }
450 
451     // restore focus-on-map setting in case ShowWithoutActivating() was called
452     gtk_window_set_focus_on_map(GTK_WINDOW(win->m_widget), true);
453 
454     return false;
455 }
456 }
457 
458 //-----------------------------------------------------------------------------
459 // "window-state-event" from m_widget
460 //-----------------------------------------------------------------------------
461 
462 extern "C" {
463 static gboolean
gtk_frame_window_state_callback(GtkWidget * WXUNUSED (widget),GdkEventWindowState * event,wxTopLevelWindow * win)464 gtk_frame_window_state_callback( GtkWidget* WXUNUSED(widget),
465                           GdkEventWindowState *event,
466                           wxTopLevelWindow *win )
467 {
468     if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED)
469         win->SetIconizeState((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) != 0);
470 
471     // if maximized bit changed and it is now set
472     if (event->changed_mask & event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
473     {
474         wxMaximizeEvent evt(win->GetId());
475         evt.SetEventObject(win);
476         win->HandleWindowEvent(evt);
477     }
478 
479     if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
480         win->m_fsIsShowing = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0;
481 
482     return false;
483 }
484 }
485 
486 //-----------------------------------------------------------------------------
487 // "notify::gtk-theme-name" from GtkSettings
488 //-----------------------------------------------------------------------------
489 
490 extern "C" {
notify_gtk_theme_name(GObject *,GParamSpec *,wxTopLevelWindowGTK * win)491 static void notify_gtk_theme_name(GObject*, GParamSpec*, wxTopLevelWindowGTK* win)
492 {
493     wxSysColourChangedEvent event;
494     event.SetEventObject(win);
495     win->HandleWindowEvent(event);
496 }
497 }
498 
499 //-----------------------------------------------------------------------------
500 
wxGetFrameExtents(GdkWindow * window,int * left,int * right,int * top,int * bottom)501 bool wxGetFrameExtents(GdkWindow* window, int* left, int* right, int* top, int* bottom)
502 {
503 #ifdef GDK_WINDOWING_X11
504     GdkDisplay* display = gdk_window_get_display(window);
505 
506 #ifdef __WXGTK3__
507     if (strcmp("GdkX11Display", g_type_name(G_TYPE_FROM_INSTANCE(display))) != 0)
508         return false;
509 #endif
510 
511     static GdkAtom property = gdk_atom_intern("_NET_FRAME_EXTENTS", false);
512     Atom xproperty = gdk_x11_atom_to_xatom_for_display(display, property);
513     Atom type;
514     int format;
515     gulong nitems, bytes_after;
516     guchar* data = NULL;
517     Status status = XGetWindowProperty(
518         GDK_DISPLAY_XDISPLAY(display),
519         GDK_WINDOW_XID(window),
520         xproperty,
521         0, 4, false, XA_CARDINAL,
522         &type, &format, &nitems, &bytes_after, &data);
523     const bool success = status == Success && data && nitems == 4;
524     if (success)
525     {
526         // We need to convert the X11 physical extents to GTK+ "logical" units
527         int scale = 1;
528 #if GTK_CHECK_VERSION(3,10,0)
529         if (wx_is_at_least_gtk3(10))
530             scale = gdk_window_get_scale_factor(window);
531 #endif
532         long* p = (long*)data;
533         if (left)   *left   = int(p[0]) / scale;
534         if (right)  *right  = int(p[1]) / scale;
535         if (top)    *top    = int(p[2]) / scale;
536         if (bottom) *bottom = int(p[3]) / scale;
537     }
538     if (data)
539         XFree(data);
540     return success;
541 #else
542     return false;
543 #endif
544 }
545 
546 #ifdef GDK_WINDOWING_X11
547 //-----------------------------------------------------------------------------
548 // "property_notify_event" from m_widget
549 //-----------------------------------------------------------------------------
550 
551 extern "C" {
property_notify_event(GtkWidget *,GdkEventProperty * event,wxTopLevelWindowGTK * win)552 static gboolean property_notify_event(
553     GtkWidget*, GdkEventProperty* event, wxTopLevelWindowGTK* win)
554 {
555     // Watch for changes to _NET_FRAME_EXTENTS property
556     static GdkAtom property = gdk_atom_intern("_NET_FRAME_EXTENTS", false);
557     if (event->state == GDK_PROPERTY_NEW_VALUE && event->atom == property)
558     {
559         if (win->m_netFrameExtentsTimerId)
560         {
561             // WM support for _NET_REQUEST_FRAME_EXTENTS is working
562             gs_requestFrameExtentsStatus = RFE_STATUS_WORKING;
563             g_source_remove(win->m_netFrameExtentsTimerId);
564             win->m_netFrameExtentsTimerId = 0;
565         }
566 
567         wxTopLevelWindowGTK::DecorSize decorSize = win->m_decorSize;
568         gs_decorCacheValid = wxGetFrameExtents(event->window,
569             &decorSize.left, &decorSize.right, &decorSize.top, &decorSize.bottom);
570 
571         win->GTKUpdateDecorSize(decorSize);
572     }
573     return false;
574 }
575 }
576 
577 extern "C" {
request_frame_extents_timeout(void * data)578 static gboolean request_frame_extents_timeout(void* data)
579 {
580     // WM support for _NET_REQUEST_FRAME_EXTENTS is broken
581     gs_requestFrameExtentsStatus = RFE_STATUS_BROKEN;
582     gdk_threads_enter();
583     wxTopLevelWindowGTK* win = static_cast<wxTopLevelWindowGTK*>(data);
584     win->m_netFrameExtentsTimerId = 0;
585     wxTopLevelWindowGTK::DecorSize decorSize = win->m_decorSize;
586     wxGetFrameExtents(gtk_widget_get_window(win->m_widget),
587         &decorSize.left, &decorSize.right, &decorSize.top, &decorSize.bottom);
588     win->GTKUpdateDecorSize(decorSize);
589     gdk_threads_leave();
590     return false;
591 }
592 }
593 #endif // GDK_WINDOWING_X11
594 
595 // ----------------------------------------------------------------------------
596 // wxTopLevelWindowGTK creation
597 // ----------------------------------------------------------------------------
598 
Init()599 void wxTopLevelWindowGTK::Init()
600 {
601     m_mainWidget = NULL;
602     m_isIconized = false;
603     m_fsIsShowing = false;
604     m_themeEnabled = true;
605     m_gdkDecor =
606     m_gdkFunc = 0;
607     m_grabbedEventLoop = NULL;
608     m_deferShow = true;
609     m_deferShowAllowed = true;
610     m_updateDecorSize = true;
611     m_netFrameExtentsTimerId = 0;
612     m_incWidth = m_incHeight = 0;
613 
614     m_urgency_hint = -2;
615 
616 #ifdef __WXGTK3__
617     m_pendingFittingClientSizeFlags = 0;
618 #endif // __WXGTK3__
619 }
620 
Create(wxWindow * parent,wxWindowID id,const wxString & title,const wxPoint & pos,const wxSize & sizeOrig,long style,const wxString & name)621 bool wxTopLevelWindowGTK::Create( wxWindow *parent,
622                                   wxWindowID id,
623                                   const wxString& title,
624                                   const wxPoint& pos,
625                                   const wxSize& sizeOrig,
626                                   long style,
627                                   const wxString &name )
628 {
629     wxSize size(sizeOrig);
630     if (!size.IsFullySpecified())
631         size.SetDefaults(GetDefaultSize());
632 
633     wxTopLevelWindows.Append( this );
634 
635     if (!PreCreation( parent, pos, size ) ||
636         !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
637     {
638         wxFAIL_MSG( wxT("wxTopLevelWindowGTK creation failed") );
639         return false;
640     }
641 
642     m_title = title;
643 
644 #ifndef __WXGTK4__
645     // Gnome uses class as display name
646     gdk_set_program_class(wxTheApp->GetAppDisplayName().utf8_str());
647 #endif
648 
649     // NB: m_widget may be !=NULL if it was created by derived class' Create,
650     //     e.g. in wxTaskBarIconAreaGTK
651     if (m_widget == NULL)
652     {
653         m_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
654         if (GetExtraStyle() & wxTOPLEVEL_EX_DIALOG)
655         {
656             // Tell WM that this is a dialog window and make it center
657             // on parent by default (this is what GtkDialog ctor does):
658             gtk_window_set_type_hint(GTK_WINDOW(m_widget),
659                                      GDK_WINDOW_TYPE_HINT_DIALOG);
660             gtk_window_set_position(GTK_WINDOW(m_widget),
661                                     GTK_WIN_POS_CENTER_ON_PARENT);
662         }
663         else
664         {
665             if (style & wxFRAME_TOOL_WINDOW)
666             {
667                 gtk_window_set_type_hint(GTK_WINDOW(m_widget),
668                                          GDK_WINDOW_TYPE_HINT_UTILITY);
669 
670                 // On some WMs, like KDE, a TOOL_WINDOW will still show
671                 // on the taskbar, but on Gnome a TOOL_WINDOW will not.
672                 // For consistency between WMs and with Windows, we
673                 // should set the NO_TASKBAR flag which will apply
674                 // the set_skip_taskbar_hint if it is available,
675                 // ensuring no taskbar entry will appear.
676                 style |= wxFRAME_NO_TASKBAR;
677             }
678         }
679 
680         g_object_ref(m_widget);
681     }
682 
683     wxWindow *topParent = wxGetTopLevelParent(m_parent);
684     if (topParent && (((GTK_IS_WINDOW(topParent->m_widget)) &&
685                        (GetExtraStyle() & wxTOPLEVEL_EX_DIALOG)) ||
686                        (style & wxFRAME_FLOAT_ON_PARENT)))
687     {
688         gtk_window_set_transient_for( GTK_WINDOW(m_widget),
689                                       GTK_WINDOW(topParent->m_widget) );
690     }
691 
692     if (style & wxFRAME_NO_TASKBAR)
693     {
694         gtk_window_set_skip_taskbar_hint(GTK_WINDOW(m_widget), TRUE);
695     }
696 
697     if (style & wxSTAY_ON_TOP)
698     {
699         gtk_window_set_keep_above(GTK_WINDOW(m_widget), TRUE);
700     }
701     if (style & wxMAXIMIZE)
702         gtk_window_maximize(GTK_WINDOW(m_widget));
703 
704 #if 0
705     if (!name.empty())
706         gtk_window_set_role( GTK_WINDOW(m_widget), wxGTK_CONV( name ) );
707 #endif
708 
709     gtk_window_set_title( GTK_WINDOW(m_widget), wxGTK_CONV( title ) );
710     gtk_widget_set_can_focus(m_widget, false);
711 
712     g_signal_connect (m_widget, "delete_event",
713                       G_CALLBACK (gtk_frame_delete_callback), this);
714 
715     // m_mainWidget is a GtkVBox, holding the bars and client area (m_wxwindow)
716     m_mainWidget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
717     gtk_widget_show( m_mainWidget );
718     gtk_widget_set_can_focus(m_mainWidget, false);
719     gtk_container_add( GTK_CONTAINER(m_widget), m_mainWidget );
720 
721     // m_wxwindow is the client area
722     m_wxwindow = wxPizza::New();
723     gtk_widget_show( m_wxwindow );
724     gtk_box_pack_start(GTK_BOX(m_mainWidget), m_wxwindow, true, true, 0);
725 
726     // we donm't allow the frame to get the focus as otherwise
727     // the frame will grab it at arbitrary focus changes
728     gtk_widget_set_can_focus(m_wxwindow, false);
729 
730     if (m_parent) m_parent->AddChild( this );
731 
732     g_signal_connect(m_wxwindow, "size_allocate",
733         G_CALLBACK(size_allocate), this);
734 
735     PostCreation();
736 
737     if (pos.IsFullySpecified())
738     {
739 #ifdef __WXGTK3__
740         GtkWindowPosition windowPos;
741         g_object_get(m_widget, "window-position", &windowPos, NULL);
742         if (windowPos == GTK_WIN_POS_NONE)
743             gtk_window_move(GTK_WINDOW(m_widget), m_x, m_y);
744 #else
745         gtk_widget_set_uposition( m_widget, m_x, m_y );
746 #endif
747     }
748 
749     // for some reported size corrections
750     g_signal_connect (m_widget, "map_event",
751                       G_CALLBACK (gtk_frame_map_callback), this);
752 
753     // for iconized state
754     g_signal_connect (m_widget, "window_state_event",
755                       G_CALLBACK (gtk_frame_window_state_callback), this);
756 
757 
758     // for wxMoveEvent
759     g_signal_connect (m_widget, "configure_event",
760                       G_CALLBACK (gtk_frame_configure_callback), this);
761 
762     // activation
763     g_signal_connect_after (m_widget, "focus_in_event",
764                       G_CALLBACK (gtk_frame_focus_in_callback), this);
765     g_signal_connect_after (m_widget, "focus_out_event",
766                       G_CALLBACK (gtk_frame_focus_out_callback), this);
767 
768     // We need to customize the default GTK+ logic for key processing to make
769     // it conforming to wxWidgets event processing order.
770     g_signal_connect (m_widget, "key_press_event",
771                       G_CALLBACK (wxgtk_tlw_key_press_event), NULL);
772 
773 #ifdef __WXGTK3__
774     const char* displayTypeName =
775         g_type_name(G_TYPE_FROM_INSTANCE(gtk_widget_get_display(m_widget)));
776 #endif
777 #ifdef GDK_WINDOWING_X11
778 #ifdef __WXGTK3__
779     if (strcmp("GdkX11Display", displayTypeName) == 0)
780 #endif
781     {
782         gtk_widget_add_events(m_widget, GDK_PROPERTY_CHANGE_MASK);
783         g_signal_connect(m_widget, "property_notify_event",
784             G_CALLBACK(property_notify_event), this);
785     }
786 #endif // GDK_WINDOWING_X11
787 
788     // translate wx decorations styles into Motif WM hints (they are recognized
789     // by other WMs as well)
790 
791     // always enable moving the window as we have no separate flag for enabling
792     // it
793     m_gdkFunc = GDK_FUNC_MOVE;
794 
795     if ( style & wxCLOSE_BOX )
796         m_gdkFunc |= GDK_FUNC_CLOSE;
797 
798     if ( style & wxMINIMIZE_BOX )
799         m_gdkFunc |= GDK_FUNC_MINIMIZE;
800 
801     if ( style & wxMAXIMIZE_BOX )
802         m_gdkFunc |= GDK_FUNC_MAXIMIZE;
803 
804     if ( (style & wxSIMPLE_BORDER) || (style & wxNO_BORDER) )
805     {
806         m_gdkDecor = 0;
807         gtk_window_set_decorated(GTK_WINDOW(m_widget), false);
808     }
809     else // have border
810     {
811         m_gdkDecor = GDK_DECOR_BORDER;
812 
813         if ( style & wxCAPTION )
814             m_gdkDecor |= GDK_DECOR_TITLE;
815 #if GTK_CHECK_VERSION(3,10,0)
816         else if (
817             strcmp("GdkWaylandDisplay", displayTypeName) == 0 &&
818             gtk_check_version(3,10,0) == NULL)
819         {
820             gtk_window_set_titlebar(GTK_WINDOW(m_widget), gtk_header_bar_new());
821         }
822 #endif
823 
824         if ( style & wxSYSTEM_MENU )
825             m_gdkDecor |= GDK_DECOR_MENU;
826 
827         if ( style & wxMINIMIZE_BOX )
828             m_gdkDecor |= GDK_DECOR_MINIMIZE;
829 
830         if ( style & wxMAXIMIZE_BOX )
831             m_gdkDecor |= GDK_DECOR_MAXIMIZE;
832 
833         if ( style & wxRESIZE_BORDER )
834         {
835            m_gdkFunc |= GDK_FUNC_RESIZE;
836            m_gdkDecor |= GDK_DECOR_RESIZEH;
837         }
838     }
839 
840     m_decorSize = GetCachedDecorSize();
841     int w, h;
842     GTKDoGetSize(&w, &h);
843 
844     if (style & wxRESIZE_BORDER)
845     {
846         gtk_window_set_default_size(GTK_WINDOW(m_widget), w, h);
847 #ifndef __WXGTK3__
848         gtk_window_set_policy(GTK_WINDOW(m_widget), 1, 1, 1);
849 #endif
850     }
851     else
852     {
853         gtk_window_set_resizable(GTK_WINDOW(m_widget), false);
854         // gtk_window_set_default_size() does not work for un-resizable windows,
855         // unless you set the size hints, but that causes Ubuntu's WM to make
856         // the window resizable even though GDK_FUNC_RESIZE is not set.
857         gtk_widget_set_size_request(m_widget, w, h);
858     }
859 
860     // Note that we need to connect after this signal in order to let the
861     // normal g_signal_connect() in wxSystemSettings code to run before our
862     // handler: this ensures that system settings cache is cleared before the
863     // user-defined wxSysColourChangedEvent handlers using wxSystemSettings
864     // methods are executed, which is important as otherwise they would use the
865     // old colours etc.
866     g_signal_connect_after(gtk_settings_get_default(), "notify::gtk-theme-name",
867         G_CALLBACK(notify_gtk_theme_name), this);
868 
869     return true;
870 }
871 
~wxTopLevelWindowGTK()872 wxTopLevelWindowGTK::~wxTopLevelWindowGTK()
873 {
874     if ( m_netFrameExtentsTimerId )
875     {
876         // Don't let the timer callback fire as the window pointer passed to it
877         // will become invalid very soon.
878         g_source_remove(m_netFrameExtentsTimerId);
879     }
880 
881     if (m_grabbedEventLoop)
882     {
883         wxFAIL_MSG(wxT("Window still grabbed"));
884         RemoveGrab();
885     }
886 
887     SendDestroyEvent();
888 
889     // it may also be GtkScrolledWindow in the case of an MDI child
890     if (GTK_IS_WINDOW(m_widget))
891     {
892         gtk_window_set_focus( GTK_WINDOW(m_widget), NULL );
893     }
894 
895     if (g_activeFrame == this)
896         g_activeFrame = NULL;
897 
898     g_signal_handlers_disconnect_by_func(
899         gtk_settings_get_default(), (void*)notify_gtk_theme_name, this);
900 }
901 
EnableCloseButton(bool enable)902 bool wxTopLevelWindowGTK::EnableCloseButton( bool enable )
903 {
904     if (enable)
905         m_gdkFunc |= GDK_FUNC_CLOSE;
906     else
907         m_gdkFunc &= ~GDK_FUNC_CLOSE;
908 
909     GdkWindow* window = gtk_widget_get_window(m_widget);
910     if (window)
911         gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc);
912 
913     return true;
914 }
915 
ShowFullScreen(bool show,long)916 bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long)
917 {
918     if (show == m_fsIsShowing)
919         return false; // return what?
920 
921     // documented behaviour is to show the window if it's still hidden when
922     // showing it full screen
923     if (show)
924         Show();
925 
926     m_fsIsShowing = show;
927 
928 #if defined(GDK_WINDOWING_X11) && !defined(__WXGTK4__)
929     GdkScreen* screen = gtk_widget_get_screen(m_widget);
930     GdkDisplay* display = gdk_screen_get_display(screen);
931     Display* xdpy = NULL;
932     Window xroot = None;
933     wxX11FullScreenMethod method = wxX11_FS_WMSPEC;
934 
935 #ifdef __WXGTK3__
936     if (strcmp("GdkX11Display", g_type_name(G_TYPE_FROM_INSTANCE(display))) == 0)
937 #endif
938     {
939         xdpy = GDK_DISPLAY_XDISPLAY(display);
940         xroot = GDK_WINDOW_XID(gdk_screen_get_root_window(screen));
941         method = wxGetFullScreenMethodX11(xdpy, (WXWindow)xroot);
942     }
943 
944     // NB: gtk_window_fullscreen() uses freedesktop.org's WMspec extensions
945     //     to switch to fullscreen, which is not always available. We must
946     //     check if WM supports the spec and use legacy methods if it
947     //     doesn't.
948     if ( method == wxX11_FS_WMSPEC )
949 #endif // GDK_WINDOWING_X11
950     {
951         if (show)
952             gtk_window_fullscreen( GTK_WINDOW( m_widget ) );
953         else
954             gtk_window_unfullscreen( GTK_WINDOW( m_widget ) );
955     }
956 #if defined(GDK_WINDOWING_X11) && !defined(__WXGTK4__)
957     else if (xdpy != NULL)
958     {
959         GdkWindow* window = gtk_widget_get_window(m_widget);
960         Window xid = GDK_WINDOW_XID(window);
961 
962         if (show)
963         {
964             GetPosition( &m_fsSaveFrame.x, &m_fsSaveFrame.y );
965             GetSize( &m_fsSaveFrame.width, &m_fsSaveFrame.height );
966 
967             wxGCC_WARNING_SUPPRESS(deprecated-declarations)
968             const int screen_width = gdk_screen_get_width(screen);
969             const int screen_height = gdk_screen_get_height(screen);
970             wxGCC_WARNING_RESTORE()
971 
972             gint client_x, client_y, root_x, root_y;
973             gint width, height;
974 
975             m_fsSaveGdkFunc = m_gdkFunc;
976             m_fsSaveGdkDecor = m_gdkDecor;
977             m_gdkFunc = m_gdkDecor = 0;
978             gdk_window_set_decorations(window, (GdkWMDecoration)0);
979             gdk_window_set_functions(window, (GdkWMFunction)0);
980 
981             gdk_window_get_origin(window, &root_x, &root_y);
982             gdk_window_get_geometry(window, &client_x, &client_y, &width, &height);
983 
984             gdk_window_move_resize(
985                 window, -client_x, -client_y, screen_width + 1, screen_height + 1);
986 
987             wxSetFullScreenStateX11(xdpy,
988                                     (WXWindow)xroot,
989                                     (WXWindow)xid,
990                                     show, &m_fsSaveFrame, method);
991         }
992         else // hide
993         {
994             m_gdkFunc = m_fsSaveGdkFunc;
995             m_gdkDecor = m_fsSaveGdkDecor;
996             gdk_window_set_decorations(window, (GdkWMDecoration)m_gdkDecor);
997             gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc);
998 
999             wxSetFullScreenStateX11(xdpy,
1000                                     (WXWindow)xroot,
1001                                     (WXWindow)xid,
1002                                     show, &m_fsSaveFrame, method);
1003 
1004             SetSize(m_fsSaveFrame.x, m_fsSaveFrame.y,
1005                     m_fsSaveFrame.width, m_fsSaveFrame.height);
1006         }
1007     }
1008 #endif // GDK_WINDOWING_X11
1009 
1010     return true;
1011 }
1012 
1013 // ----------------------------------------------------------------------------
1014 // overridden wxWindow methods
1015 // ----------------------------------------------------------------------------
1016 
Refresh(bool WXUNUSED (eraseBackground),const wxRect * WXUNUSED (rect))1017 void wxTopLevelWindowGTK::Refresh( bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect) )
1018 {
1019     wxCHECK_RET( m_widget, wxT("invalid frame") );
1020 
1021     gtk_widget_queue_draw( m_widget );
1022 
1023     GdkWindow* window = NULL;
1024     if (m_wxwindow)
1025         window = gtk_widget_get_window(m_wxwindow);
1026     if (window)
1027         gdk_window_invalidate_rect(window, NULL, true);
1028 }
1029 
1030 #if defined(__WXGTK3__) && defined(GDK_WINDOWING_X11)
1031 // Check conditions under which GTK will use Client Side Decorations with X11
isUsingCSD(GtkWidget * widget)1032 static bool isUsingCSD(GtkWidget* widget)
1033 {
1034     const char* csd = getenv("GTK_CSD");
1035     if (csd == NULL || strcmp(csd, "1") != 0)
1036         return false;
1037 
1038     GdkScreen* screen = gtk_widget_get_screen(widget);
1039     if (!gdk_screen_is_composited(screen))
1040         return false;
1041 
1042     GdkAtom atom = gdk_atom_intern_static_string("_GTK_FRAME_EXTENTS");
1043     if (!gdk_x11_screen_supports_net_wm_hint(screen, atom))
1044         return false;
1045 
1046     if (gdk_screen_get_rgba_visual(screen) == NULL)
1047         return false;
1048 
1049     return true;
1050 }
1051 #endif // __WXGTK3__ && GDK_WINDOWING_X11
1052 
Show(bool show)1053 bool wxTopLevelWindowGTK::Show( bool show )
1054 {
1055     wxCHECK_MSG(m_widget, false, "invalid frame");
1056 
1057 #ifdef GDK_WINDOWING_X11
1058     bool deferShow = show && !m_isShown && !m_isIconized && m_deferShow;
1059     if (deferShow)
1060     {
1061         GdkScreen* screen = gtk_widget_get_screen(m_widget);
1062         deferShow = m_deferShowAllowed &&
1063             // Assume size (from cache or wxPersistentTLW) is correct.
1064             // Avoids problems when WM initially provides an incorrect value
1065             // for extents, then corrects it later.
1066             m_decorSize.top == 0 &&
1067 
1068             gs_requestFrameExtentsStatus != RFE_STATUS_BROKEN &&
1069             !gtk_widget_get_realized(m_widget) &&
1070 #ifdef __WXGTK3__
1071             strcmp("GdkX11Screen", g_type_name(G_TYPE_FROM_INSTANCE(screen))) == 0 &&
1072 #endif
1073             g_signal_handler_find(m_widget,
1074                 GSignalMatchType(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA),
1075                 g_signal_lookup("property_notify_event", GTK_TYPE_WIDGET),
1076                 0, NULL, NULL, this);
1077 #ifdef __WXGTK3__
1078         // Don't defer with CSD, it isn't needed and causes pixman errors
1079         if (deferShow)
1080             deferShow = !isUsingCSD(m_widget);
1081 #endif
1082         if (deferShow)
1083         {
1084             GdkAtom atom = gdk_atom_intern("_NET_REQUEST_FRAME_EXTENTS", false);
1085             deferShow = gdk_x11_screen_supports_net_wm_hint(screen, atom) != 0;
1086 
1087             // If _NET_REQUEST_FRAME_EXTENTS not supported, don't allow changes
1088             // to m_decorSize, it breaks saving/restoring window size with
1089             // GetSize()/SetSize() because it makes window bigger between each
1090             // restore and save.
1091             m_updateDecorSize = deferShow;
1092         }
1093 
1094         m_deferShow = deferShow;
1095     }
1096     if (deferShow)
1097     {
1098         // Initial show. If WM supports _NET_REQUEST_FRAME_EXTENTS, defer
1099         // calling gtk_widget_show() until _NET_FRAME_EXTENTS property
1100         // notification is received, so correct frame extents are known.
1101         // This allows resizing m_widget to keep the overall size in sync with
1102         // what wxWidgets expects it to be without an obvious change in the
1103         // window size immediately after it becomes visible.
1104 
1105         // Realize m_widget, so m_widget->window can be used. Realizing normally
1106         // causes the widget tree to be size_allocated, which generates size
1107         // events in the wrong order. However, the size_allocates will not be
1108         // done if the allocation is not the default (1,1).
1109         GtkAllocation alloc;
1110         gtk_widget_get_allocation(m_widget, &alloc);
1111         const int alloc_width = alloc.width;
1112         if (alloc_width == 1)
1113         {
1114             alloc.width = 2;
1115             gtk_widget_set_allocation(m_widget, &alloc);
1116         }
1117         gtk_widget_realize(m_widget);
1118         if (alloc_width == 1)
1119         {
1120             alloc.width = 1;
1121             gtk_widget_set_allocation(m_widget, &alloc);
1122         }
1123 
1124         // send _NET_REQUEST_FRAME_EXTENTS
1125         XClientMessageEvent xevent;
1126         memset(&xevent, 0, sizeof(xevent));
1127         xevent.type = ClientMessage;
1128         GdkWindow* window = gtk_widget_get_window(m_widget);
1129         xevent.window = GDK_WINDOW_XID(window);
1130         xevent.message_type = gdk_x11_atom_to_xatom_for_display(
1131             gdk_window_get_display(window),
1132             gdk_atom_intern("_NET_REQUEST_FRAME_EXTENTS", false));
1133         xevent.format = 32;
1134         Display* display = GDK_DISPLAY_XDISPLAY(gdk_window_get_display(window));
1135         XSendEvent(display, DefaultRootWindow(display), false,
1136             SubstructureNotifyMask | SubstructureRedirectMask,
1137             (XEvent*)&xevent);
1138 
1139         if (gs_requestFrameExtentsStatus == RFE_STATUS_UNKNOWN)
1140         {
1141             // if WM does not respond to request within 1 second,
1142             // we assume support for _NET_REQUEST_FRAME_EXTENTS is not working
1143             m_netFrameExtentsTimerId =
1144                 g_timeout_add(1000, request_frame_extents_timeout, this);
1145         }
1146 
1147         // defer calling gtk_widget_show()
1148         m_isShown = true;
1149         return true;
1150     }
1151 #endif // GDK_WINDOWING_X11
1152 
1153     if (show && !gtk_widget_get_realized(m_widget))
1154     {
1155         // size_allocate signals occur in reverse order (bottom to top).
1156         // Things work better if the initial wxSizeEvents are sent (from the
1157         // top down), before the initial size_allocate signals occur.
1158         SendSizeEvent();
1159 
1160 #ifdef __WXGTK3__
1161         GTKSizeRevalidate();
1162 #endif
1163     }
1164 
1165     bool change = base_type::Show(show);
1166 
1167 #ifdef __WXGTK3__
1168     if (change && show)
1169     {
1170         // We may need to redo it after showing the window.
1171         GTKUpdateClientSizeIfNecessary();
1172     }
1173 
1174     if (m_needSizeEvent)
1175     {
1176         m_needSizeEvent = false;
1177         SendSizeEvent();
1178     }
1179 #endif
1180 
1181     if (change && !show)
1182     {
1183         // Generate wxEVT_KILL_FOCUS for the currently focused control
1184         // immediately (i.e. without waiting until the window is destroyed and
1185         // doing it from its dtor), as it could be too late to execute the
1186         // handler for this event, or other events triggered by receiving it,
1187         // by then because the wxTLW will have been half-destroyed by then.
1188         if (GTK_IS_WINDOW(m_widget))
1189         {
1190             gtk_window_set_focus( GTK_WINDOW(m_widget), NULL );
1191         }
1192 
1193         // make sure window has a non-default position, so when it is shown
1194         // again, it won't be repositioned by WM as if it were a new window
1195         // Note that this must be done _after_ the window is hidden.
1196         gtk_window_move((GtkWindow*)m_widget, m_x, m_y);
1197     }
1198 
1199     return change;
1200 }
1201 
ShowWithoutActivating()1202 void wxTopLevelWindowGTK::ShowWithoutActivating()
1203 {
1204     if (!m_isShown)
1205     {
1206         gtk_window_set_focus_on_map(GTK_WINDOW(m_widget), false);
1207         Show(true);
1208     }
1209 }
1210 
Raise()1211 void wxTopLevelWindowGTK::Raise()
1212 {
1213     gtk_window_present( GTK_WINDOW( m_widget ) );
1214 }
1215 
DoMoveWindow(int WXUNUSED (x),int WXUNUSED (y),int WXUNUSED (width),int WXUNUSED (height))1216 void wxTopLevelWindowGTK::DoMoveWindow(int WXUNUSED(x), int WXUNUSED(y), int WXUNUSED(width), int WXUNUSED(height) )
1217 {
1218     wxFAIL_MSG( wxT("DoMoveWindow called for wxTopLevelWindowGTK") );
1219 }
1220 
1221 // ----------------------------------------------------------------------------
1222 // window geometry
1223 // ----------------------------------------------------------------------------
1224 
GTKDoGetSize(int * width,int * height) const1225 void wxTopLevelWindowGTK::GTKDoGetSize(int *width, int *height) const
1226 {
1227     wxSize size(m_width, m_height);
1228     if (!HasClientDecor(m_widget))
1229     {
1230         size.x -= m_decorSize.left + m_decorSize.right;
1231         size.y -= m_decorSize.top + m_decorSize.bottom;
1232     }
1233     if (size.x < 0) size.x = 0;
1234     if (size.y < 0) size.y = 0;
1235     if (width)  *width  = size.x;
1236     if (height) *height = size.y;
1237 }
1238 
DoSetSize(int x,int y,int width,int height,int sizeFlags)1239 void wxTopLevelWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
1240 {
1241     wxCHECK_RET( m_widget, wxT("invalid frame") );
1242 
1243     // deal with the position first
1244     int old_x = m_x;
1245     int old_y = m_y;
1246 
1247     if ( !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) )
1248     {
1249         // -1 means "use existing" unless the flag above is specified
1250         if ( x != -1 )
1251             m_x = x;
1252         if ( y != -1 )
1253             m_y = y;
1254     }
1255     else // wxSIZE_ALLOW_MINUS_ONE
1256     {
1257         m_x = x;
1258         m_y = y;
1259     }
1260 
1261     const wxSize oldSize(m_width, m_height);
1262     if (width >= 0)
1263         m_width = width;
1264     if (height >= 0)
1265         m_height = height;
1266     ConstrainSize();
1267     if (m_width < 1) m_width = 1;
1268     if (m_height < 1) m_height = 1;
1269 
1270     if ( m_x != old_x || m_y != old_y )
1271     {
1272         gtk_window_move( GTK_WINDOW(m_widget), m_x, m_y );
1273         wxMoveEvent event(wxPoint(m_x, m_y), GetId());
1274         event.SetEventObject(this);
1275         HandleWindowEvent(event);
1276     }
1277 
1278     if (m_width != oldSize.x || m_height != oldSize.y)
1279     {
1280         m_deferShowAllowed = true;
1281         m_useCachedClientSize = false;
1282 
1283 #ifdef __WXGTK3__
1284         // Reset pending client size, it is not relevant any more and shouldn't
1285         // be set when the window is shown, as we don't want it to replace the
1286         // size explicitly specified here. Note that we do still want to set
1287         // the minimum client size, as increasing the total size shouldn't
1288         // allow shrinking the frame beyond its minimum fitting size.
1289         //
1290         // Also note that if we're called from WXSetInitialFittingClientSize()
1291         // itself, this will be overwritten again with the pending flags when
1292         // we return.
1293         m_pendingFittingClientSizeFlags &= ~wxSIZE_SET_CURRENT;
1294 #endif // __WXGTK3__
1295 
1296         int w, h;
1297         GTKDoGetSize(&w, &h);
1298         gtk_window_resize(GTK_WINDOW(m_widget), w, h);
1299         if (!gtk_window_get_resizable(GTK_WINDOW(m_widget)))
1300             gtk_widget_set_size_request(GTK_WIDGET(m_widget), w, h);
1301 
1302         DoGetClientSize(&m_clientWidth, &m_clientHeight);
1303         wxSizeEvent event(GetSize(), GetId());
1304         event.SetEventObject(this);
1305         HandleWindowEvent(event);
1306     }
1307 }
1308 
1309 extern "C" {
reset_size_request(void * data)1310 static gboolean reset_size_request(void* data)
1311 {
1312     gtk_widget_set_size_request(GTK_WIDGET(data), -1, -1);
1313     g_object_unref(data);
1314     return false;
1315 }
1316 }
1317 
DoSetClientSize(int width,int height)1318 void wxTopLevelWindowGTK::DoSetClientSize(int width, int height)
1319 {
1320     base_type::DoSetClientSize(width, height);
1321 
1322     // Since client size is being explicitly set, don't change it later
1323     // Has to be done after calling base because it calls SetSize,
1324     // which sets this true
1325     m_deferShowAllowed = false;
1326 
1327     if (m_wxwindow)
1328     {
1329         // If window is not resizable or not yet shown, set size request on
1330         // client widget, to make it more likely window will get correct size
1331         // even if our decorations size cache is incorrect (as it will be before
1332         // showing first TLW).
1333         if (!gtk_window_get_resizable(GTK_WINDOW(m_widget)))
1334         {
1335             gtk_widget_set_size_request(m_widget, -1, -1);
1336             gtk_widget_set_size_request(m_wxwindow, m_clientWidth, m_clientHeight);
1337         }
1338         else if (!IsShown())
1339         {
1340             gtk_widget_set_size_request(m_wxwindow, m_clientWidth, m_clientHeight);
1341             // Cancel size request at next idle to allow resizing
1342             g_idle_add_full(G_PRIORITY_LOW - 1, reset_size_request, m_wxwindow, NULL);
1343             g_object_ref(m_wxwindow);
1344         }
1345     }
1346 }
1347 
DoGetClientSize(int * width,int * height) const1348 void wxTopLevelWindowGTK::DoGetClientSize( int *width, int *height ) const
1349 {
1350     wxCHECK_RET(m_widget, "invalid frame");
1351 
1352     if ( IsIconized() )
1353     {
1354         // for consistency with wxMSW, client area is supposed to be empty for
1355         // the iconized windows
1356         if ( width )
1357             *width = 0;
1358         if ( height )
1359             *height = 0;
1360     }
1361     else if (m_useCachedClientSize)
1362         base_type::DoGetClientSize(width, height);
1363     else
1364     {
1365         int w = m_width - (m_decorSize.left + m_decorSize.right);
1366         int h = m_height - (m_decorSize.top + m_decorSize.bottom);
1367         if (w < 0) w = 0;
1368         if (h < 0) h = 0;
1369         if (width) *width = w;
1370         if (height) *height = h;
1371     }
1372 }
1373 
DoSetSizeHints(int minW,int minH,int maxW,int maxH,int incW,int incH)1374 void wxTopLevelWindowGTK::DoSetSizeHints( int minW, int minH,
1375                                           int maxW, int maxH,
1376                                           int incW, int incH )
1377 {
1378     base_type::DoSetSizeHints(minW, minH, maxW, maxH, incW, incH);
1379 
1380     if (!HasFlag(wxRESIZE_BORDER))
1381     {
1382         // It's not useful to set size hints for the windows which can't be
1383         // resized anyhow, and it even results in warnings from Gnome window
1384         // manager, so just don't do it (but notice that it's not an error to
1385         // call this for non-resizeable windows, e.g. because it's done
1386         // implicitly by SetSizerAndFit() which is useful even in this case).
1387         return;
1388     }
1389 
1390     m_incWidth = incW;
1391     m_incHeight = incH;
1392 
1393     const wxSize minSize = GetMinSize();
1394     const wxSize maxSize = GetMaxSize();
1395     GdkGeometry hints;
1396     // always set both min and max hints, otherwise GTK will
1397     // make assumptions we don't want about the unset values
1398     int hints_mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
1399     hints.min_width = 1;
1400     hints.min_height = 1;
1401     // using INT_MAX for size will lead to integer overflow with HiDPI scaling
1402     hints.max_width = INT_MAX / 16;
1403     hints.max_height = INT_MAX / 16;
1404     int decorSize_x;
1405     int decorSize_y;
1406     if (HasClientDecor(m_widget))
1407     {
1408         decorSize_x = 0;
1409         decorSize_y = 0;
1410     }
1411     else
1412     {
1413         decorSize_x = m_decorSize.left + m_decorSize.right;
1414         decorSize_y = m_decorSize.top + m_decorSize.bottom;
1415     }
1416     if (minSize.x > decorSize_x)
1417         hints.min_width = minSize.x - decorSize_x;
1418     if (minSize.y > decorSize_y)
1419         hints.min_height = minSize.y - decorSize_y;
1420     if (maxSize.x > 0)
1421     {
1422         hints.max_width = maxSize.x - decorSize_x;
1423         if (hints.max_width < hints.min_width)
1424             hints.max_width = hints.min_width;
1425     }
1426     if (maxSize.y > 0)
1427     {
1428         hints.max_height = maxSize.y - decorSize_y;
1429         if (hints.max_height < hints.min_height)
1430             hints.max_height = hints.min_height;
1431     }
1432     if (incW > 0 || incH > 0)
1433     {
1434         hints_mask |= GDK_HINT_RESIZE_INC;
1435         hints.width_inc  = incW > 0 ? incW : 1;
1436         hints.height_inc = incH > 0 ? incH : 1;
1437     }
1438     gtk_window_set_geometry_hints(
1439         (GtkWindow*)m_widget, NULL, &hints, (GdkWindowHints)hints_mask);
1440 }
1441 
GTKUpdateDecorSize(const DecorSize & decorSize)1442 void wxTopLevelWindowGTK::GTKUpdateDecorSize(const DecorSize& decorSize)
1443 {
1444     if (!IsMaximized() && !IsFullScreen())
1445         GetCachedDecorSize() = decorSize;
1446 
1447     if (HasClientDecor(m_widget))
1448     {
1449         m_decorSize = decorSize;
1450         return;
1451     }
1452 #ifdef GDK_WINDOWING_X11
1453     if (m_updateDecorSize && memcmp(&m_decorSize, &decorSize, sizeof(DecorSize)))
1454     {
1455         m_useCachedClientSize = false;
1456         const wxSize diff(
1457             decorSize.left - m_decorSize.left + decorSize.right - m_decorSize.right,
1458             decorSize.top - m_decorSize.top + decorSize.bottom - m_decorSize.bottom);
1459         m_decorSize = decorSize;
1460         bool resized = false;
1461         if (m_minWidth > 0 || m_minHeight > 0 || m_maxWidth > 0 || m_maxHeight > 0)
1462         {
1463             // update size hints, they depend on m_decorSize
1464             if (!m_deferShow)
1465             {
1466                 // if size hints match old size, assume hints were set to
1467                 // maintain current client size, and adjust hints accordingly
1468                 if (m_minWidth == m_width) m_minWidth += diff.x;
1469                 if (m_maxWidth == m_width) m_maxWidth += diff.x;
1470                 if (m_minHeight == m_height) m_minHeight += diff.y;
1471                 if (m_maxHeight == m_height) m_maxHeight += diff.y;
1472             }
1473             DoSetSizeHints(m_minWidth, m_minHeight, m_maxWidth, m_maxHeight, m_incWidth, m_incHeight);
1474         }
1475         if (m_deferShow)
1476         {
1477             // keep overall size unchanged by shrinking m_widget
1478             int w, h;
1479             GTKDoGetSize(&w, &h);
1480             // but not if size would be less than minimum, it won't take effect
1481             if (w >= m_minWidth - (decorSize.left + decorSize.right) &&
1482                 h >= m_minHeight - (decorSize.top + decorSize.bottom))
1483             {
1484                 gtk_window_resize(GTK_WINDOW(m_widget), w, h);
1485                 if (!gtk_window_get_resizable(GTK_WINDOW(m_widget)))
1486                     gtk_widget_set_size_request(GTK_WIDGET(m_widget), w, h);
1487                 resized = true;
1488             }
1489         }
1490         if (!resized)
1491         {
1492             // adjust overall size to match change in frame extents
1493             m_width  += diff.x;
1494             m_height += diff.y;
1495             if (m_width  < 1) m_width  = 1;
1496             if (m_height < 1) m_height = 1;
1497             m_clientWidth = 0;
1498             gtk_widget_queue_resize(m_wxwindow);
1499         }
1500     }
1501     if (m_deferShow)
1502     {
1503         // gtk_widget_show() was deferred, do it now
1504         m_deferShow = false;
1505         DoGetClientSize(&m_clientWidth, &m_clientHeight);
1506         SendSizeEvent();
1507 
1508 #ifdef __WXGTK3__
1509         GTKSizeRevalidate();
1510 #endif
1511         if (!m_isShown)
1512             return;
1513 
1514         gtk_widget_show(m_widget);
1515 
1516 #ifdef __WXGTK3__
1517         if (m_needSizeEvent)
1518         {
1519             m_needSizeEvent = false;
1520             SendSizeEvent();
1521         }
1522 #endif
1523 
1524         GTKDoAfterShow();
1525     }
1526 #endif // GDK_WINDOWING_X11
1527 }
1528 
GetCachedDecorSize()1529 wxTopLevelWindowGTK::DecorSize& wxTopLevelWindowGTK::GetCachedDecorSize()
1530 {
1531     static DecorSize size[8];
1532 
1533     int index = 0;
1534     // title bar
1535     if (m_gdkDecor & (GDK_DECOR_MENU | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE | GDK_DECOR_TITLE))
1536         index = 1;
1537     // border
1538     if (m_gdkDecor & GDK_DECOR_BORDER)
1539         index |= 2;
1540     // utility window decor can be different
1541     if (m_windowStyle & wxFRAME_TOOL_WINDOW)
1542         index |= 4;
1543     return size[index];
1544 }
1545 
GTKDoAfterShow()1546 void wxTopLevelWindowGTK::GTKDoAfterShow()
1547 {
1548 #ifdef __WXGTK3__
1549     // Set the client size again if necessary, we should be able to do it
1550     // correctly by now as the style cache should be up to date.
1551     GTKUpdateClientSizeIfNecessary();
1552 #endif // __WXGTK3__
1553 
1554     wxShowEvent showEvent(GetId(), true);
1555     showEvent.SetEventObject(this);
1556     HandleWindowEvent(showEvent);
1557 }
1558 
1559 #ifdef __WXGTK3__
1560 
GTKUpdateClientSizeIfNecessary()1561 void wxTopLevelWindowGTK::GTKUpdateClientSizeIfNecessary()
1562 {
1563     if ( m_pendingFittingClientSizeFlags )
1564     {
1565         WXSetInitialFittingClientSize(m_pendingFittingClientSizeFlags);
1566 
1567         m_pendingFittingClientSizeFlags = 0;
1568     }
1569 }
1570 
SetMinSize(const wxSize & minSize)1571 void wxTopLevelWindowGTK::SetMinSize(const wxSize& minSize)
1572 {
1573     wxTopLevelWindowBase::SetMinSize(minSize);
1574 
1575     // Explicitly set minimum size should override the pending size, if any.
1576     m_pendingFittingClientSizeFlags &= ~wxSIZE_SET_MIN;
1577 }
1578 
WXSetInitialFittingClientSize(int flags)1579 void wxTopLevelWindowGTK::WXSetInitialFittingClientSize(int flags)
1580 {
1581     // In any case, update the size immediately.
1582     wxTopLevelWindowBase::WXSetInitialFittingClientSize(flags);
1583 
1584     // But if we're not shown yet, the fitting size may be wrong because GTK
1585     // style cache hasn't been updated yet and we need to do it again when the
1586     // window becomes visible as we can be sure that by then we'll be able to
1587     // compute the best size correctly.
1588     if ( !IsShown() )
1589         m_pendingFittingClientSizeFlags = flags;
1590 }
1591 
1592 #endif // __WXGTK3__
1593 
1594 // ----------------------------------------------------------------------------
1595 // frame title/icon
1596 // ----------------------------------------------------------------------------
1597 
SetTitle(const wxString & title)1598 void wxTopLevelWindowGTK::SetTitle( const wxString &title )
1599 {
1600     wxCHECK_RET(m_widget, "invalid frame");
1601 
1602     if ( title == m_title )
1603         return;
1604 
1605     m_title = title;
1606 
1607     gtk_window_set_title( GTK_WINDOW(m_widget), wxGTK_CONV( title ) );
1608 }
1609 
SetIcons(const wxIconBundle & icons)1610 void wxTopLevelWindowGTK::SetIcons( const wxIconBundle &icons )
1611 {
1612     base_type::SetIcons(icons);
1613 
1614     // Setting icons before window is realized can cause a GTK assertion if
1615     // another TLW is realized before this one, and it has this one as its
1616     // transient parent. The life demo exibits this problem.
1617     if (m_widget && gtk_widget_get_realized(m_widget))
1618     {
1619         GList* list = NULL;
1620         for (size_t i = icons.GetIconCount(); i--;)
1621             list = g_list_prepend(list, icons.GetIconByIndex(i).GetPixbuf());
1622         gtk_window_set_icon_list(GTK_WINDOW(m_widget), list);
1623         g_list_free(list);
1624     }
1625 }
1626 
1627 // ----------------------------------------------------------------------------
1628 // frame state: maximized/iconized/normal
1629 // ----------------------------------------------------------------------------
1630 
Maximize(bool maximize)1631 void wxTopLevelWindowGTK::Maximize(bool maximize)
1632 {
1633     if (maximize)
1634         gtk_window_maximize( GTK_WINDOW( m_widget ) );
1635     else
1636         gtk_window_unmaximize( GTK_WINDOW( m_widget ) );
1637 }
1638 
IsMaximized() const1639 bool wxTopLevelWindowGTK::IsMaximized() const
1640 {
1641     GdkWindow* window = NULL;
1642     if (m_widget)
1643         window = gtk_widget_get_window(m_widget);
1644     return window && (gdk_window_get_state(window) & GDK_WINDOW_STATE_MAXIMIZED);
1645 }
1646 
Restore()1647 void wxTopLevelWindowGTK::Restore()
1648 {
1649     // "Present" seems similar enough to "restore"
1650     gtk_window_present( GTK_WINDOW( m_widget ) );
1651 }
1652 
Iconize(bool iconize)1653 void wxTopLevelWindowGTK::Iconize( bool iconize )
1654 {
1655     if (iconize)
1656         gtk_window_iconify( GTK_WINDOW( m_widget ) );
1657     else
1658         gtk_window_deiconify( GTK_WINDOW( m_widget ) );
1659 }
1660 
IsIconized() const1661 bool wxTopLevelWindowGTK::IsIconized() const
1662 {
1663     return m_isIconized;
1664 }
1665 
SetIconizeState(bool iconize)1666 void wxTopLevelWindowGTK::SetIconizeState(bool iconize)
1667 {
1668     if ( iconize != m_isIconized )
1669     {
1670         /*
1671             We get a configure-event _before_ the window is iconized with dummy
1672             (0, 0) coordinates. Unfortunately we can't just ignore this event
1673             when we get it, because we can't know if we're going to be iconized
1674             or not: even remembering that we should be soon in Iconize() itself
1675             doesn't help due to the crazy sequence of events generated by GTK,
1676             e.g. under X11 for the sequence of Iconize() and Show() calls we
1677             get the following events with GTK2:
1678 
1679             - window-state changes to GDK_WINDOW_STATE_ICONIFIED | WITHDRAWN
1680             - window-state changes to just GDK_WINDOW_STATE_ICONIFIED
1681             - map event
1682             - window-state changes to normal
1683             - configure event with normal size
1684             - window-state changes to GDK_WINDOW_STATE_ICONIFIED
1685             - configure event with normal size
1686             - configure event with (0, 0) size
1687             - window-state changes to normal (yes, again)
1688             - configure event with (0, 0) size
1689             - window-state changes to GDK_WINDOW_STATE_ICONIFIED
1690 
1691             So even though we could ignore the first (0, 0) configure event, we
1692             still wouldn't be able to ignore the second one, happening after
1693             the inexplicable switch to normal state.
1694 
1695             For completeness, with GTK3 the sequence of events is simpler, but
1696             still very unhelpful for our purposes:
1697 
1698             - window-state changes to GDK_WINDOW_STATE_ICONIFIED
1699             - window-state changes to normal
1700             - map event
1701             - configure event with normal size
1702             - configure event with normal size (yes, because one is not enough)
1703             - configure event with (0, 0) size
1704             - configure event with (0, 0) size (because why not have 2 of them)
1705             - window-state changes to GDK_WINDOW_STATE_ICONIFIED
1706 
1707             Here again we have (0, 0) configure events happening _after_ the
1708             window-state switch to normal, that would reset our flag.
1709 
1710             In conclusion, we have no choice but to assume that the window got
1711             really moved, but at least we can remember its previous position in
1712             order to restore it here if it turns out that it was just iconized.
1713          */
1714         if ( iconize )
1715         {
1716             if ( wxPoint(m_x, m_y) == wxPoint(0, 0) )
1717             {
1718                 m_x = m_lastPos.x;
1719                 m_y = m_lastPos.y;
1720 
1721                 // This is not really necessary, but seems tidier.
1722                 m_lastPos = wxPoint();
1723             }
1724         }
1725 
1726         m_isIconized = iconize;
1727         (void)SendIconizeEvent(iconize);
1728     }
1729 }
1730 
AddGrab()1731 void wxTopLevelWindowGTK::AddGrab()
1732 {
1733     if (!m_grabbedEventLoop)
1734     {
1735         wxGUIEventLoop eventLoop;
1736         m_grabbedEventLoop = &eventLoop;
1737         gtk_grab_add( m_widget );
1738         eventLoop.Run();
1739         gtk_grab_remove( m_widget );
1740         m_grabbedEventLoop = NULL;
1741     }
1742 }
1743 
RemoveGrab()1744 void wxTopLevelWindowGTK::RemoveGrab()
1745 {
1746     if (m_grabbedEventLoop)
1747     {
1748         m_grabbedEventLoop->Exit();
1749         m_grabbedEventLoop = NULL;
1750     }
1751 }
1752 
IsGrabbed() const1753 bool wxTopLevelWindowGTK::IsGrabbed() const
1754 {
1755     return m_grabbedEventLoop != NULL;
1756 }
1757 
IsActive()1758 bool wxTopLevelWindowGTK::IsActive()
1759 {
1760     return (this == (wxTopLevelWindowGTK*)g_activeFrame);
1761 }
1762 
RequestUserAttention(int flags)1763 void wxTopLevelWindowGTK::RequestUserAttention(int flags)
1764 {
1765     bool new_hint_value = false;
1766 
1767     // FIXME: This is a workaround to focus handling problem
1768     // If RequestUserAttention is called for example right after a wxSleep, OnInternalIdle
1769     // hasn't yet been processed, and the internal focus system is not up to date yet.
1770     // YieldFor(wxEVT_CATEGORY_UI) ensures the processing of it (hopefully it
1771     // won't have side effects) - MR
1772     wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI);
1773 
1774     if(m_urgency_hint >= 0)
1775         g_source_remove(m_urgency_hint);
1776 
1777     m_urgency_hint = -2;
1778 
1779     if( gtk_widget_get_realized(m_widget) && !IsActive() )
1780     {
1781         new_hint_value = true;
1782 
1783         if (flags & wxUSER_ATTENTION_INFO)
1784         {
1785             m_urgency_hint = g_timeout_add(5000, (GSourceFunc)gtk_frame_urgency_timer_callback, this);
1786         } else {
1787             m_urgency_hint = -1;
1788         }
1789     }
1790 
1791     gtk_window_set_urgency_hint(GTK_WINDOW(m_widget), new_hint_value);
1792 }
1793 
SetWindowStyleFlag(long style)1794 void wxTopLevelWindowGTK::SetWindowStyleFlag( long style )
1795 {
1796     // Store which styles were changed
1797     long styleChanges = style ^ m_windowStyle;
1798 
1799     // Process wxWindow styles. This also updates the internal variable
1800     // Therefore m_windowStyle bits carry now the _new_ style values
1801     wxWindow::SetWindowStyleFlag(style);
1802 
1803     // just return for now if widget does not exist yet
1804     if (!m_widget)
1805         return;
1806 
1807     if ( styleChanges & wxSTAY_ON_TOP )
1808     {
1809         gtk_window_set_keep_above(GTK_WINDOW(m_widget),
1810                                   m_windowStyle & wxSTAY_ON_TOP);
1811     }
1812 
1813     if ( styleChanges & wxFRAME_NO_TASKBAR )
1814     {
1815         gtk_window_set_skip_taskbar_hint(GTK_WINDOW(m_widget),
1816                                          m_windowStyle & wxFRAME_NO_TASKBAR);
1817     }
1818 }
1819 
SetTransparent(wxByte alpha)1820 bool wxTopLevelWindowGTK::SetTransparent(wxByte alpha)
1821 {
1822     if (m_widget == NULL)
1823         return false;
1824 
1825 #ifdef __WXGTK3__
1826 #if GTK_CHECK_VERSION(3,8,0)
1827     if (wx_is_at_least_gtk3(8))
1828     {
1829         gtk_widget_set_opacity(m_widget, alpha / 255.0);
1830     }
1831     else
1832 #endif // GTK+ 3.8+
1833     {
1834 #ifndef __WXGTK4__
1835         wxGCC_WARNING_SUPPRESS(deprecated-declarations)
1836         gtk_window_set_opacity(GTK_WINDOW(m_widget), alpha / 255.0);
1837         wxGCC_WARNING_RESTORE()
1838 #endif
1839     }
1840 
1841     return true;
1842 #else // !__WXGTK3__
1843 
1844 #if GTK_CHECK_VERSION(2,12,0)
1845     if (wx_is_at_least_gtk2(12))
1846     {
1847         gtk_window_set_opacity(GTK_WINDOW(m_widget), alpha / 255.0);
1848         return true;
1849     }
1850 #endif // GTK_CHECK_VERSION(2,12,0)
1851 #ifdef GDK_WINDOWING_X11
1852     GdkWindow* window = gtk_widget_get_window(m_widget);
1853     if (window == NULL)
1854         return false;
1855 
1856     Display* dpy = GDK_WINDOW_XDISPLAY(window);
1857     Window win = GDK_WINDOW_XID(window);
1858 
1859     if (alpha == 0xff)
1860         XDeleteProperty(dpy, win, XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False));
1861     else
1862     {
1863         long opacity = alpha * 0x1010101L;
1864         XChangeProperty(dpy, win, XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False),
1865                         XA_CARDINAL, 32, PropModeReplace,
1866                         (unsigned char *) &opacity, 1L);
1867     }
1868     XSync(dpy, False);
1869     return true;
1870 #else // !GDK_WINDOWING_X11
1871     return false;
1872 #endif // GDK_WINDOWING_X11 / !GDK_WINDOWING_X11
1873 #endif // __WXGTK3__/!__WXGTK3__
1874 }
1875 
CanSetTransparent()1876 bool wxTopLevelWindowGTK::CanSetTransparent()
1877 {
1878     // allow to override automatic detection as it's far from perfect
1879     const wxString SYSOPT_TRANSPARENT = "gtk.tlw.can-set-transparent";
1880     if ( wxSystemOptions::HasOption(SYSOPT_TRANSPARENT) )
1881     {
1882         return wxSystemOptions::GetOptionInt(SYSOPT_TRANSPARENT) != 0;
1883     }
1884 
1885 #ifdef __WXGTK4__
1886     return gdk_display_is_composited(gtk_widget_get_display(m_widget)) != 0;
1887 #else
1888 #if GTK_CHECK_VERSION(2,10,0)
1889     if (wx_is_at_least_gtk2(10))
1890     {
1891         wxGCC_WARNING_SUPPRESS(deprecated-declarations)
1892         return gtk_widget_is_composited(m_widget) != 0;
1893         wxGCC_WARNING_RESTORE()
1894     }
1895     else
1896 #endif // In case of lower versions than gtk+-2.10.0 we could look for _NET_WM_CM_Sn ourselves
1897     {
1898         return false;
1899     }
1900 #endif
1901 
1902 #if 0 // Don't be optimistic here for the sake of wxAUI
1903     int opcode, event, error;
1904     // Check for the existence of a RGBA visual instead?
1905     return XQueryExtension(gdk_x11_get_default_xdisplay (),
1906                            "Composite", &opcode, &event, &error);
1907 #endif
1908 }
1909 
GetDefaultAttributes() const1910 wxVisualAttributes wxTopLevelWindowGTK::GetDefaultAttributes() const
1911 {
1912     wxVisualAttributes attrs(GetClassDefaultAttributes());
1913 #ifdef __WXGTK3__
1914     wxGtkStyleContext().AddWindow().Bg(attrs.colBg);
1915 #endif
1916     return attrs;
1917 }
1918