1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
21 #include <unx/gtk/gtkdata.hxx>
22 #include <unx/gtk/gtkinst.hxx>
23 #include <unx/gtk/gtksys.hxx>
24 #include <unx/gtk/gtkbackend.hxx>
25 #include <osl/module.h>
27 GtkSalSystem
*GtkSalSystem::GetSingleton()
29 static GtkSalSystem
*pSingleton
= new GtkSalSystem();
33 SalSystem
*GtkInstance::CreateSalSystem()
35 return GtkSalSystem::GetSingleton();
38 GtkSalSystem::GtkSalSystem()
40 mpDisplay
= gdk_display_get_default();
41 #if !GTK_CHECK_VERSION(4, 0, 0)
42 countScreenMonitors();
44 // rhbz#1285356, native look will be gtk2, which crashes
45 // when gtk3 is already loaded. Until there is a solution
46 // java-side force look and feel to something that doesn't
47 // crash when we are using gtk3
48 setenv("STOC_FORCE_SYSTEM_LAF", "true", 1);
51 GtkSalSystem::~GtkSalSystem()
58 struct GdkRectangleCoincidentLess
60 // fdo#78799 - detect and elide overlaying monitors of different sizes
61 bool operator()(GdkRectangle
const& rLeft
, GdkRectangle
const& rRight
)
69 struct GdkRectangleCoincident
71 // fdo#78799 - detect and elide overlaying monitors of different sizes
72 bool operator()(GdkRectangle
const& rLeft
, GdkRectangle
const& rRight
)
76 && rLeft
.y
== rRight
.y
83 #if !GTK_CHECK_VERSION(4, 0, 0)
85 * GtkSalSystem::countScreenMonitors()
87 * This method builds the vector which allows us to map from VCL's
88 * idea of linear integer ScreenNumber to gtk+'s rather more
89 * complicated screen + monitor concept.
92 GtkSalSystem::countScreenMonitors()
94 maScreenMonitors
.clear();
95 for (gint i
= 0; i
< gdk_display_get_n_screens(mpDisplay
); i
++)
97 GdkScreen
* const pScreen(gdk_display_get_screen(mpDisplay
, i
));
98 gint
nMonitors(pScreen
? gdk_screen_get_n_monitors(pScreen
) : 0);
101 std::vector
<GdkRectangle
> aGeometries
;
102 aGeometries
.reserve(nMonitors
);
103 for (gint
j(0); j
!= nMonitors
; ++j
)
105 GdkRectangle aGeometry
;
106 gdk_screen_get_monitor_geometry(pScreen
, j
, &aGeometry
);
107 aGeometries
.push_back(aGeometry
);
109 std::sort(aGeometries
.begin(), aGeometries
.end(),
110 GdkRectangleCoincidentLess());
111 const std::vector
<GdkRectangle
>::iterator
aUniqueEnd(
112 std::unique(aGeometries
.begin(), aGeometries
.end(),
113 GdkRectangleCoincident()));
114 nMonitors
= std::distance(aGeometries
.begin(), aUniqueEnd
);
116 maScreenMonitors
.emplace_back(pScreen
, nMonitors
);
122 GtkSalSystem::getXScreenFromDisplayScreen(unsigned int nScreen
)
124 if (!DLSYM_GDK_IS_X11_DISPLAY(mpDisplay
))
125 return SalX11Screen (0);
127 #if GTK_CHECK_VERSION(4, 0, 0)
128 GdkX11Screen
*pScreen
= gdk_x11_display_get_screen(mpDisplay
);
132 GdkScreen
*pScreen
= getScreenMonitorFromIdx (nScreen
, nMonitor
);
134 return SalX11Screen (0);
136 return SalX11Screen(gdk_x11_screen_get_screen_number(pScreen
));
139 #if !GTK_CHECK_VERSION(4, 0, 0)
141 GtkSalSystem::getScreenMonitorFromIdx (int nIdx
, gint
&nMonitor
)
143 GdkScreen
*pScreen
= nullptr;
144 for (auto const& screenMonitor
: maScreenMonitors
)
146 pScreen
= screenMonitor
.first
;
149 if (nIdx
>= screenMonitor
.second
)
150 nIdx
-= screenMonitor
.second
;
156 // handle invalid monitor indexes as non-existent screens
157 if (nMonitor
< 0 || (pScreen
&& nMonitor
>= gdk_screen_get_n_monitors (pScreen
)))
164 GtkSalSystem::getScreenIdxFromPtr (GdkScreen
*pScreen
)
167 for (auto const& screenMonitor
: maScreenMonitors
)
169 if (screenMonitor
.first
== pScreen
)
171 nIdx
+= screenMonitor
.second
;
173 g_warning ("failed to find screen %p", pScreen
);
177 int GtkSalSystem::getScreenMonitorIdx (GdkScreen
*pScreen
,
180 // TODO: this will fail horribly for exotic combinations like two
181 // monitors in mirror mode and one extra. Hopefully such
182 // abominations are not used (or, even better, not possible) in
184 return getScreenIdxFromPtr (pScreen
) +
185 gdk_screen_get_monitor_at_point (pScreen
, nX
, nY
);
189 unsigned int GtkSalSystem::GetDisplayScreenCount()
191 #if GTK_CHECK_VERSION(4, 0, 0)
192 return g_list_model_get_n_items(gdk_display_get_monitors(mpDisplay
));
195 (void)getScreenMonitorFromIdx (G_MAXINT
, nMonitor
);
196 return G_MAXINT
- nMonitor
;
200 bool GtkSalSystem::IsUnifiedDisplay()
202 #if !GTK_CHECK_VERSION(4, 0, 0)
203 return gdk_display_get_n_screens (mpDisplay
) == 1;
209 unsigned int GtkSalSystem::GetDisplayBuiltInScreen()
211 #if GTK_CHECK_VERSION(4, 0, 0)
212 #if defined(GDK_WINDOWING_X11)
213 if (DLSYM_GDK_IS_X11_DISPLAY(mpDisplay
))
215 GdkMonitor
* pPrimary
= gdk_x11_display_get_primary_monitor(mpDisplay
);
216 GListModel
* pList
= gdk_display_get_monitors(mpDisplay
);
218 while (gpointer pElem
= g_list_model_get_item(pList
, nIndex
))
220 if (pElem
== pPrimary
)
226 // nothing for wayland ?, hope for the best that its at index 0
228 #else // !GTK_CHECK_VERSION(4, 0, 0)
229 GdkScreen
*pDefault
= gdk_display_get_default_screen (mpDisplay
);
230 int idx
= getScreenIdxFromPtr (pDefault
);
231 return idx
+ gdk_screen_get_primary_monitor(pDefault
);
235 tools::Rectangle
GtkSalSystem::GetDisplayScreenPosSizePixel(unsigned int nScreen
)
238 #if GTK_CHECK_VERSION(4, 0, 0)
239 GListModel
* pList
= gdk_display_get_monitors(mpDisplay
);
240 GdkMonitor
* pMonitor
= static_cast<GdkMonitor
*>(g_list_model_get_item(pList
, nScreen
));
242 return tools::Rectangle();
243 gdk_monitor_get_geometry(pMonitor
, &aRect
);
247 pScreen
= getScreenMonitorFromIdx (nScreen
, nMonitor
);
249 return tools::Rectangle();
250 gdk_screen_get_monitor_geometry (pScreen
, nMonitor
, &aRect
);
252 return tools::Rectangle (Point(aRect
.x
, aRect
.y
), Size(aRect
.width
, aRect
.height
));
255 // convert ~ to indicate mnemonic to '_'
256 static OString
MapToGtkAccelerator(const OUString
&rStr
)
258 return OUStringToOString(rStr
.replaceFirst("~", "_"), RTL_TEXTENCODING_UTF8
);
261 #if GTK_CHECK_VERSION(4, 0, 0)
267 GMainLoop
* pLoop
= nullptr;
268 gint nResponseId
= GTK_RESPONSE_NONE
;
269 gulong nSignalResponseId
= 0;
270 gulong nSignalCloseRequestId
= 0;
272 static gboolean
DialogClose(GtkWindow
* pDialog
, gpointer
/*data*/)
274 gtk_dialog_response(GTK_DIALOG(pDialog
), GTK_RESPONSE_CANCEL
);
278 static void DialogResponse(GtkDialog
* pDialog
, gint nResponseId
, gpointer data
)
280 DialogLoop
* pDialogLoop
= static_cast<DialogLoop
*>(data
);
281 g_signal_handler_disconnect(pDialog
, pDialogLoop
->nSignalResponseId
);
282 g_signal_handler_disconnect(pDialog
, pDialogLoop
->nSignalCloseRequestId
);
283 pDialogLoop
->nResponseId
= nResponseId
;
284 g_main_loop_quit(pDialogLoop
->pLoop
);
287 int run(GtkDialog
*pDialog
)
289 nSignalResponseId
= g_signal_connect(pDialog
, "response", G_CALLBACK(DialogResponse
), this);
290 nSignalCloseRequestId
= g_signal_connect(pDialog
, "close-request", G_CALLBACK(DialogClose
), this);
291 gtk_window_present(GTK_WINDOW(pDialog
));
292 pLoop
= g_main_loop_new(nullptr, false);
293 main_loop_run(pLoop
);
294 g_main_loop_unref(pLoop
);
301 gint
gtk_dialog_run(GtkDialog
* pDialog
)
303 DialogLoop aDialogLoop
;
304 return aDialogLoop
.run(pDialog
);
309 int GtkSalSystem::ShowNativeDialog (const OUString
& rTitle
, const OUString
& rMessage
,
310 const std::vector
< OUString
>& rButtonNames
)
312 OString
aTitle (OUStringToOString (rTitle
, RTL_TEXTENCODING_UTF8
));
313 OString
aMessage (OUStringToOString (rMessage
, RTL_TEXTENCODING_UTF8
));
315 GtkDialog
*pDialog
= GTK_DIALOG (
316 g_object_new (GTK_TYPE_MESSAGE_DIALOG
,
317 "title", aTitle
.getStr(),
318 "message-type", int(GTK_MESSAGE_WARNING
),
319 "text", aMessage
.getStr(),
322 for (auto const& buttonName
: rButtonNames
)
323 gtk_dialog_add_button (pDialog
, MapToGtkAccelerator(buttonName
).getStr(), nButton
++);
324 gtk_dialog_set_default_response (pDialog
, 0/*nDefaultButton*/);
326 nButton
= gtk_dialog_run (pDialog
);
330 #if !GTK_CHECK_VERSION(4, 0, 0)
331 gtk_widget_destroy(GTK_WIDGET(pDialog
));
333 gtk_window_destroy(GTK_WINDOW(pDialog
));
339 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */