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