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