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