Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / unx / gtk3 / gtk3gtksys.cxx
blob379c87343ae87cb75799a1965d061322de0921db
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <gtk/gtk.h>
21 #include <unx/gtk/gtkinst.hxx>
22 #include <unx/gtk/gtksys.hxx>
23 #include <unx/gtk/gtkbackend.hxx>
24 #include <osl/module.h>
26 GtkSalSystem *GtkSalSystem::GetSingleton()
28 static GtkSalSystem *pSingleton = new GtkSalSystem();
29 return pSingleton;
32 SalSystem *GtkInstance::CreateSalSystem()
34 return GtkSalSystem::GetSingleton();
37 GtkSalSystem::GtkSalSystem() : SalGenericSystem()
39 mpDisplay = gdk_display_get_default();
40 countScreenMonitors();
41 // rhbz#1285356, native look will be gtk2, which crashes
42 // when gtk3 is already loaded. Until there is a solution
43 // java-side force look and feel to something that doesn't
44 // crash when we are using gtk3
45 setenv("STOC_FORCE_SYSTEM_LAF", "true", 1);
48 GtkSalSystem::~GtkSalSystem()
52 namespace
55 struct GdkRectangleCoincidentLess
57 // fdo#78799 - detect and elide overlaying monitors of different sizes
58 bool operator()(GdkRectangle const& rLeft, GdkRectangle const& rRight)
60 return
61 rLeft.x < rRight.x
62 || rLeft.y < rRight.y
66 struct GdkRectangleCoincident
68 // fdo#78799 - detect and elide overlaying monitors of different sizes
69 bool operator()(GdkRectangle const& rLeft, GdkRectangle const& rRight)
71 return
72 rLeft.x == rRight.x
73 && rLeft.y == rRight.y
80 /**
81 * GtkSalSystem::countScreenMonitors()
83 * This method builds the vector which allows us to map from VCL's
84 * idea of linear integer ScreenNumber to gtk+'s rather more
85 * complicated screen + monitor concept.
87 void
88 GtkSalSystem::countScreenMonitors()
90 maScreenMonitors.clear();
91 for (gint i = 0; i < gdk_display_get_n_screens(mpDisplay); i++)
93 GdkScreen* const pScreen(gdk_display_get_screen(mpDisplay, i));
94 gint nMonitors(pScreen ? gdk_screen_get_n_monitors(pScreen) : 0);
95 if (nMonitors > 1)
97 std::vector<GdkRectangle> aGeometries;
98 aGeometries.reserve(nMonitors);
99 for (gint j(0); j != nMonitors; ++j)
101 GdkRectangle aGeometry;
102 gdk_screen_get_monitor_geometry(pScreen, j, &aGeometry);
103 aGeometries.push_back(aGeometry);
105 std::sort(aGeometries.begin(), aGeometries.end(),
106 GdkRectangleCoincidentLess());
107 const std::vector<GdkRectangle>::iterator aUniqueEnd(
108 std::unique(aGeometries.begin(), aGeometries.end(),
109 GdkRectangleCoincident()));
110 nMonitors = std::distance(aGeometries.begin(), aUniqueEnd);
112 maScreenMonitors.emplace_back(pScreen, nMonitors);
116 SalX11Screen
117 GtkSalSystem::getXScreenFromDisplayScreen(unsigned int nScreen)
119 gint nMonitor;
121 GdkScreen *pScreen = getScreenMonitorFromIdx (nScreen, nMonitor);
122 if (!pScreen)
123 return SalX11Screen (0);
124 if (!DLSYM_GDK_IS_X11_DISPLAY(mpDisplay))
125 return SalX11Screen (0);
126 return SalX11Screen (gdk_x11_screen_get_screen_number (pScreen));
129 GdkScreen *
130 GtkSalSystem::getScreenMonitorFromIdx (int nIdx, gint &nMonitor)
132 GdkScreen *pScreen = nullptr;
133 for (auto const& screenMonitor : maScreenMonitors)
135 pScreen = screenMonitor.first;
136 if (!pScreen)
137 break;
138 if (nIdx >= screenMonitor.second)
139 nIdx -= screenMonitor.second;
140 else
141 break;
143 nMonitor = nIdx;
145 // handle invalid monitor indexes as non-existent screens
146 if (nMonitor < 0 || (pScreen && nMonitor >= gdk_screen_get_n_monitors (pScreen)))
147 pScreen = nullptr;
149 return pScreen;
153 GtkSalSystem::getScreenIdxFromPtr (GdkScreen *pScreen)
155 int nIdx = 0;
156 for (auto const& screenMonitor : maScreenMonitors)
158 if (screenMonitor.first == pScreen)
159 return nIdx;
160 nIdx += screenMonitor.second;
162 g_warning ("failed to find screen %p", pScreen);
163 return 0;
166 int GtkSalSystem::getScreenMonitorIdx (GdkScreen *pScreen,
167 int nX, int nY)
169 // TODO: this will fail horribly for exotic combinations like two
170 // monitors in mirror mode and one extra. Hopefully such
171 // abominations are not used (or, even better, not possible) in
172 // practice .-)
173 return getScreenIdxFromPtr (pScreen) +
174 gdk_screen_get_monitor_at_point (pScreen, nX, nY);
177 unsigned int GtkSalSystem::GetDisplayScreenCount()
179 gint nMonitor;
180 (void)getScreenMonitorFromIdx (G_MAXINT, nMonitor);
181 return G_MAXINT - nMonitor;
184 bool GtkSalSystem::IsUnifiedDisplay()
186 return gdk_display_get_n_screens (mpDisplay) == 1;
189 namespace {
190 int _fallback_get_primary_monitor (GdkScreen *pScreen)
192 // Use monitor name as primacy heuristic
193 int max = gdk_screen_get_n_monitors (pScreen);
194 for (int i = 0; i < max; ++i)
196 char *name = gdk_screen_get_monitor_plug_name (pScreen, i);
197 bool bLaptop = (name && !g_ascii_strncasecmp (name, "LVDS", 4));
198 g_free (name);
199 if (bLaptop)
200 return i;
202 return 0;
205 int _get_primary_monitor (GdkScreen *pScreen)
207 static int (*get_fn) (GdkScreen *) = nullptr;
208 get_fn = gdk_screen_get_primary_monitor;
209 // Perhaps we have a newer gtk+ with this symbol:
210 if (!get_fn)
212 get_fn = reinterpret_cast<int(*)(GdkScreen*)>(osl_getAsciiFunctionSymbol(nullptr,
213 "gdk_screen_get_primary_monitor"));
215 if (!get_fn)
216 get_fn = _fallback_get_primary_monitor;
217 if (get_fn)
218 return get_fn (pScreen);
219 else
220 return 0;
222 } // end anonymous namespace
224 unsigned int GtkSalSystem::GetDisplayBuiltInScreen()
226 GdkScreen *pDefault = gdk_display_get_default_screen (mpDisplay);
227 int idx = getScreenIdxFromPtr (pDefault);
228 return idx + _get_primary_monitor (pDefault);
231 tools::Rectangle GtkSalSystem::GetDisplayScreenPosSizePixel (unsigned int nScreen)
233 gint nMonitor;
234 GdkScreen *pScreen;
235 GdkRectangle aRect;
236 pScreen = getScreenMonitorFromIdx (nScreen, nMonitor);
237 if (!pScreen)
238 return tools::Rectangle();
239 gdk_screen_get_monitor_geometry (pScreen, nMonitor, &aRect);
240 return tools::Rectangle (Point(aRect.x, aRect.y), Size(aRect.width, aRect.height));
243 // convert ~ to indicate mnemonic to '_'
244 static OString MapToGtkAccelerator(const OUString &rStr)
246 return OUStringToOString(rStr.replaceFirst("~", "_"), RTL_TEXTENCODING_UTF8);
249 int GtkSalSystem::ShowNativeDialog (const OUString& rTitle, const OUString& rMessage,
250 const std::vector< OUString >& rButtonNames)
252 OString aTitle (OUStringToOString (rTitle, RTL_TEXTENCODING_UTF8));
253 OString aMessage (OUStringToOString (rMessage, RTL_TEXTENCODING_UTF8));
255 GtkDialog *pDialog = GTK_DIALOG (
256 g_object_new (GTK_TYPE_MESSAGE_DIALOG,
257 "title", aTitle.getStr(),
258 "message-type", int(GTK_MESSAGE_WARNING),
259 "text", aMessage.getStr(),
260 nullptr));
261 int nButton = 0;
262 for (auto const& buttonName : rButtonNames)
263 gtk_dialog_add_button (pDialog, MapToGtkAccelerator(buttonName).getStr(), nButton++);
264 gtk_dialog_set_default_response (pDialog, 0/*nDefaultButton*/);
266 nButton = gtk_dialog_run (pDialog);
267 if (nButton < 0)
268 nButton = -1;
270 gtk_widget_destroy (GTK_WIDGET (pDialog));
272 return nButton;
275 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */