1 ///////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/display.cpp
3 // Author:      Paul Cornett
4 // Created:     2014-04-17
5 // Copyright:   (c) 2014 Paul Cornett
6 // Licence:     wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8 
9 #include "wx/wxprec.h"
10 
11 #include "wx/private/display.h"
12 #if wxUSE_DISPLAY
13     #include "wx/window.h"
14 #endif
15 
16 #include "wx/gtk/private/wrapgtk.h"
17 #ifdef GDK_WINDOWING_X11
18     #ifndef __WXGTK4__
19         #include "wx/unix/private/displayx11.h"
20 
21         #define wxGTK_HAVE_X11_DISPLAY
22     #endif
23 
24     #include <gdk/gdkx.h>
25 #endif
26 
27 // This file is not used at all when using Win32.
28 #if !defined(GDK_WINDOWING_WIN32)
29 
30 GdkWindow* wxGetTopLevelGDK();
31 
32 // There are 2 quite different implementations here: one for GTK+ 4 and the
33 // other one for the previous versions.
34 
35 #ifdef __WXGTK4__
36 
GetDisplay()37 static inline GdkDisplay* GetDisplay()
38 {
39     return gdk_window_get_display(wxGetTopLevelGDK());
40 }
41 
42 // This class is always defined as it's used for the main display even when
43 // wxUSE_DISPLAY == 0.
44 class wxDisplayImplGTK : public wxDisplayImpl
45 {
46     typedef wxDisplayImpl base_type;
47 public:
48     wxDisplayImplGTK(unsigned i);
49     virtual wxRect GetGeometry() const wxOVERRIDE;
50     virtual wxRect GetClientArea() const wxOVERRIDE;
51     virtual int GetDepth() const wxOVERRIDE;
52     virtual double GetScaleFactor() const wxOVERRIDE;
53 
54 #if wxUSE_DISPLAY
55     virtual bool IsPrimary() const wxOVERRIDE;
56     virtual wxArrayVideoModes GetModes(const wxVideoMode& mode) const wxOVERRIDE;
57     virtual wxVideoMode GetCurrentMode() const wxOVERRIDE;
58     virtual bool ChangeMode(const wxVideoMode& mode) wxOVERRIDE;
59 #endif // wxUSE_DISPLAY
60 
61     GdkMonitor* const m_monitor;
62 };
63 
64 // This class is only defined when we're built with full display support.
65 #if wxUSE_DISPLAY
66 class wxDisplayFactoryGTK: public wxDisplayFactory
67 {
68 public:
69     virtual wxDisplayImpl* CreateDisplay(unsigned n) wxOVERRIDE;
70     virtual unsigned GetCount() wxOVERRIDE;
71     virtual int GetFromPoint(const wxPoint& pt) wxOVERRIDE;
72     virtual int GetFromWindow(const wxWindow* win) wxOVERRIDE;
73 };
74 
CreateDisplay(unsigned n)75 wxDisplayImpl* wxDisplayFactoryGTK::CreateDisplay(unsigned n)
76 {
77     return new wxDisplayImplGTK(n);
78 }
79 
GetCount()80 unsigned wxDisplayFactoryGTK::GetCount()
81 {
82     return gdk_display_get_n_monitors(::GetDisplay());
83 }
84 
GetFromPoint(const wxPoint & pt)85 int wxDisplayFactoryGTK::GetFromPoint(const wxPoint& pt)
86 {
87     GdkRectangle rect;
88     GdkDisplay* display = ::GetDisplay();
89     GdkMonitor* monitor = gdk_display_get_monitor_at_point(display, pt.x, pt.y);
90     gdk_monitor_get_geometry(monitor, &rect);
91     if (wxRect(rect.x, rect.y, rect.width, rect.height).Contains(pt))
92     {
93         for (unsigned i = gdk_display_get_n_monitors(display); i--;)
94         {
95             if (gdk_display_get_monitor(display, i) == monitor)
96                 return i;
97         }
98     }
99     return wxNOT_FOUND;
100 }
101 
GetFromWindow(const wxWindow * win)102 int wxDisplayFactoryGTK::GetFromWindow(const wxWindow* win)
103 {
104     if (win && win->m_widget)
105     {
106         GdkDisplay* display = gtk_widget_get_display(win->m_widget);
107         GdkMonitor* monitor;
108         if (GdkWindow* window = gtk_widget_get_window(win->m_widget))
109             monitor = gdk_display_get_monitor_at_window(display, window);
110         else
111             monitor = gdk_display_get_primary_monitor(display);
112 
113         for (unsigned i = gdk_display_get_n_monitors(display); i--;)
114         {
115             if (gdk_display_get_monitor(display, i) == monitor)
116                 return i;
117         }
118     }
119     return wxNOT_FOUND;
120 }
121 #endif // wxUSE_DISPLAY
122 
wxDisplayImplGTK(unsigned i)123 wxDisplayImplGTK::wxDisplayImplGTK(unsigned i)
124     : base_type(i)
125     , m_monitor(gdk_display_get_monitor(GetDisplay(), i))
126 {
127 }
128 
GetGeometry() const129 wxRect wxDisplayImplGTK::GetGeometry() const
130 {
131     GdkRectangle rect;
132     gdk_monitor_get_geometry(m_monitor, &rect);
133     return wxRect(rect.x, rect.y, rect.width, rect.height);
134 }
135 
GetClientArea() const136 wxRect wxDisplayImplGTK::GetClientArea() const
137 {
138     GdkRectangle rect;
139     gdk_monitor_get_workarea(m_monitor, &rect);
140     return wxRect(rect.x, rect.y, rect.width, rect.height);
141 }
142 
GetDepth() const143 int wxDisplayImplGTK::GetDepth() const
144 {
145     return 24;
146 }
147 
GetScaleFactor() const148 double wxDisplayImplGTK::GetScaleFactor() const
149 {
150     return gdk_monitor_get_scale_factor(m_monitor);
151 }
152 
153 #if wxUSE_DISPLAY
IsPrimary() const154 bool wxDisplayImplGTK::IsPrimary() const
155 {
156     return gdk_monitor_is_primary(m_monitor) != 0;
157 }
158 
159 wxArrayVideoModes
GetModes(const wxVideoMode & WXUNUSED (mode)) const160 wxDisplayImplGTK::GetModes(const wxVideoMode& WXUNUSED(mode)) const
161 {
162     return wxArrayVideoModes();
163 }
164 
GetCurrentMode() const165 wxVideoMode wxDisplayImplGTK::GetCurrentMode() const
166 {
167     return wxVideoMode();
168 }
169 
ChangeMode(const wxVideoMode & WXUNUSED (mode))170 bool wxDisplayImplGTK::ChangeMode(const wxVideoMode& WXUNUSED(mode))
171 {
172     return false;
173 }
174 #endif // wxUSE_DISPLAY
175 
176 #else // !__WXGTK4__
177 
178 #if defined(__WXGTK3__) && defined(GDK_WINDOWING_X11)
179 
wxIsX11GDKScreen(GdkScreen * screen)180 static inline bool wxIsX11GDKScreen(GdkScreen* screen)
181 {
182     return strcmp("GdkX11Screen", g_type_name(G_TYPE_FROM_INSTANCE(screen))) == 0;
183 }
184 
185 #else // !__WXGTK3__
186 
wx_gdk_screen_get_primary_monitor(GdkScreen * screen)187 static inline int wx_gdk_screen_get_primary_monitor(GdkScreen* screen)
188 {
189     int monitor = 0;
190 #if GTK_CHECK_VERSION(2,20,0)
191     if (wx_is_at_least_gtk2(20))
192         monitor = gdk_screen_get_primary_monitor(screen);
193 #endif
194     return monitor;
195 }
196 #define gdk_screen_get_primary_monitor wx_gdk_screen_get_primary_monitor
197 
wxIsX11GDKScreen(GdkScreen * WXUNUSED (screen))198 static inline bool wxIsX11GDKScreen(GdkScreen* WXUNUSED(screen))
199 {
200     return true;
201 }
202 
203 #endif // __WXGTK3__/!__WXGTK3__
204 
205 static inline void
wx_gdk_screen_get_monitor_workarea(GdkScreen * screen,int monitor,GdkRectangle * dest)206 wx_gdk_screen_get_monitor_workarea(GdkScreen* screen, int monitor, GdkRectangle* dest)
207 {
208     wxGCC_WARNING_SUPPRESS(deprecated-declarations)
209 #if GTK_CHECK_VERSION(3,4,0)
210     if (gtk_check_version(3,4,0) == NULL)
211         gdk_screen_get_monitor_workarea(screen, monitor, dest);
212     else
213 #endif
214     {
215         gdk_screen_get_monitor_geometry(screen, monitor, dest);
216 #ifdef wxGTK_HAVE_X11_DISPLAY
217         if ( wxIsX11GDKScreen(screen) )
218         {
219             GdkRectangle rect = { 0, 0, 0, 0 };
220             wxGetWorkAreaX11(GDK_SCREEN_XSCREEN(screen),
221                 rect.x, rect.y, rect.width, rect.height);
222             // in case _NET_WORKAREA result is too large
223             if (rect.width && rect.height)
224                 gdk_rectangle_intersect(dest, &rect, dest);
225         }
226 #endif // wxGTK_HAVE_X11_DISPLAY
227     }
228     wxGCC_WARNING_RESTORE()
229 }
230 #define gdk_screen_get_monitor_workarea wx_gdk_screen_get_monitor_workarea
231 
GetScreen()232 static inline GdkScreen* GetScreen()
233 {
234     return gdk_window_get_screen(wxGetTopLevelGDK());
235 }
236 
237 class wxDisplayImplGTK : public wxDisplayImpl
238 {
239     typedef wxDisplayImpl base_type;
240 public:
241     wxDisplayImplGTK(unsigned i);
242     virtual wxRect GetGeometry() const wxOVERRIDE;
243     virtual wxRect GetClientArea() const wxOVERRIDE;
244     virtual int GetDepth() const wxOVERRIDE;
245 #if GTK_CHECK_VERSION(3,10,0)
246     virtual double GetScaleFactor() const wxOVERRIDE;
247 #endif // GTK+ 3.10
248 
249 #if wxUSE_DISPLAY
250     virtual bool IsPrimary() const wxOVERRIDE;
251     virtual wxArrayVideoModes GetModes(const wxVideoMode& mode) const wxOVERRIDE;
252     virtual wxVideoMode GetCurrentMode() const wxOVERRIDE;
253     virtual bool ChangeMode(const wxVideoMode& mode) wxOVERRIDE;
254 #endif // wxUSE_DISPLAY
255 
256     GdkScreen* const m_screen;
257 };
258 
259 #if wxUSE_DISPLAY
260 class wxDisplayFactoryGTK: public wxDisplayFactory
261 {
262 public:
263     virtual wxDisplayImpl* CreateDisplay(unsigned n) wxOVERRIDE;
264     virtual unsigned GetCount() wxOVERRIDE;
265     virtual int GetFromPoint(const wxPoint& pt) wxOVERRIDE;
266     virtual int GetFromWindow(const wxWindow* win) wxOVERRIDE;
267 };
268 
269 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
270 
CreateDisplay(unsigned n)271 wxDisplayImpl* wxDisplayFactoryGTK::CreateDisplay(unsigned n)
272 {
273     return new wxDisplayImplGTK(n);
274 }
275 
GetCount()276 unsigned wxDisplayFactoryGTK::GetCount()
277 {
278     return gdk_screen_get_n_monitors(GetScreen());
279 }
280 
GetFromPoint(const wxPoint & pt)281 int wxDisplayFactoryGTK::GetFromPoint(const wxPoint& pt)
282 {
283     GdkRectangle rect;
284     GdkScreen* screen = GetScreen();
285     int monitor = gdk_screen_get_monitor_at_point(screen, pt.x, pt.y);
286     gdk_screen_get_monitor_geometry(screen, monitor, &rect);
287     if (!wxRect(rect.x, rect.y, rect.width, rect.height).Contains(pt))
288         monitor = wxNOT_FOUND;
289     return monitor;
290 }
291 
GetFromWindow(const wxWindow * win)292 int wxDisplayFactoryGTK::GetFromWindow(const wxWindow* win)
293 {
294     int monitor = wxNOT_FOUND;
295     if (win && win->m_widget)
296     {
297         GdkScreen* screen = gtk_widget_get_screen(win->m_widget);
298         if (GdkWindow* window = gtk_widget_get_window(win->m_widget))
299             monitor = gdk_screen_get_monitor_at_window(screen, window);
300         else
301             monitor = gdk_screen_get_primary_monitor(screen);
302     }
303     return monitor;
304 }
305 #endif // wxUSE_DISPLAY
306 
wxDisplayImplGTK(unsigned i)307 wxDisplayImplGTK::wxDisplayImplGTK(unsigned i)
308     : base_type(i)
309     , m_screen(GetScreen())
310 {
311 }
312 
GetGeometry() const313 wxRect wxDisplayImplGTK::GetGeometry() const
314 {
315     GdkRectangle rect;
316     gdk_screen_get_monitor_geometry(m_screen, m_index, &rect);
317     return wxRect(rect.x, rect.y, rect.width, rect.height);
318 }
319 
GetClientArea() const320 wxRect wxDisplayImplGTK::GetClientArea() const
321 {
322     GdkRectangle rect;
323     gdk_screen_get_monitor_workarea(m_screen, m_index, &rect);
324     return wxRect(rect.x, rect.y, rect.width, rect.height);
325 }
326 
GetDepth() const327 int wxDisplayImplGTK::GetDepth() const
328 {
329     // TODO: How to get the depth of the specific display?
330     return gdk_visual_get_depth(gdk_window_get_visual(wxGetTopLevelGDK()));
331 }
332 
333 #if GTK_CHECK_VERSION(3,10,0)
GetScaleFactor() const334 double wxDisplayImplGTK::GetScaleFactor() const
335 {
336     if ( gtk_check_version(3,10,0) == NULL )
337         return gdk_screen_get_monitor_scale_factor(m_screen, m_index);
338 
339     return 1.0;
340 }
341 #endif // GTK+ 3.10
342 
343 #if wxUSE_DISPLAY
IsPrimary() const344 bool wxDisplayImplGTK::IsPrimary() const
345 {
346     return gdk_screen_get_primary_monitor(m_screen) == int(m_index);
347 }
348 
GetModes(const wxVideoMode & mode) const349 wxArrayVideoModes wxDisplayImplGTK::GetModes(const wxVideoMode& mode) const
350 {
351     wxArrayVideoModes modes;
352 #ifdef wxGTK_HAVE_X11_DISPLAY
353     if ( wxIsX11GDKScreen(m_screen) )
354     {
355         Display* display = GDK_DISPLAY_XDISPLAY(gdk_screen_get_display(m_screen));
356 #ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
357         int nScreen = gdk_x11_screen_get_screen_number(m_screen);
358         modes = wxXF86VidMode_GetModes(mode, display, nScreen);
359 #else
360         modes = wxX11_GetModes(this, mode, display);
361 #endif
362     }
363 #else
364     wxUnusedVar(mode);
365 #endif
366     return modes;
367 }
368 
GetCurrentMode() const369 wxVideoMode wxDisplayImplGTK::GetCurrentMode() const
370 {
371     wxVideoMode mode;
372 #if defined(wxGTK_HAVE_X11_DISPLAY) && defined(HAVE_X11_EXTENSIONS_XF86VMODE_H)
373     if ( wxIsX11GDKScreen(m_screen) )
374     {
375         Display* display = GDK_DISPLAY_XDISPLAY(gdk_screen_get_display(m_screen));
376         int nScreen = gdk_x11_screen_get_screen_number(m_screen);
377         mode = wxXF86VidMode_GetCurrentMode(display, nScreen);
378     }
379 #endif
380     return mode;
381 }
382 
ChangeMode(const wxVideoMode & mode)383 bool wxDisplayImplGTK::ChangeMode(const wxVideoMode& mode)
384 {
385     bool success = false;
386 #if defined(wxGTK_HAVE_X11_DISPLAY) && defined(HAVE_X11_EXTENSIONS_XF86VMODE_H)
387     if ( wxIsX11GDKScreen(m_screen) )
388     {
389         Display* display = GDK_DISPLAY_XDISPLAY(gdk_screen_get_display(m_screen));
390         int nScreen = gdk_x11_screen_get_screen_number(m_screen);
391         success = wxXF86VidMode_ChangeMode(mode, display, nScreen);
392     }
393 #else
394     wxUnusedVar(mode);
395 #endif
396     return success;
397 }
398 
wxGCC_WARNING_RESTORE()399 wxGCC_WARNING_RESTORE()
400 
401 #endif // wxUSE_DISPLAY
402 
403 #endif // __WXGTK4__/!__WXGTK4__
404 
405 #if wxUSE_DISPLAY
406 
407 wxDisplayFactory* wxDisplay::CreateFactory()
408 {
409     return new wxDisplayFactoryGTK;
410 }
411 
412 #else // !wxUSE_DISPLAY
413 
414 class wxDisplayFactorySingleGTK : public wxDisplayFactorySingle
415 {
416 protected:
417     virtual wxDisplayImpl *CreateSingleDisplay()
418     {
419         return new wxDisplayImplGTK(0);
420     }
421 };
422 
423 wxDisplayFactory* wxDisplay::CreateFactory()
424 {
425     return new wxDisplayFactorySingleGTK;
426 }
427 
428 #endif // wxUSE_DISPLAY/!wxUSE_DISPLAY
429 
430 #endif // !defined(GDK_WINDOWING_WIN32)
431