diff options
Diffstat (limited to 'vcl/unx/gtk3/gtksys.cxx')
-rw-r--r-- | vcl/unx/gtk3/gtksys.cxx | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/vcl/unx/gtk3/gtksys.cxx b/vcl/unx/gtk3/gtksys.cxx new file mode 100644 index 000000000..37e32b28b --- /dev/null +++ b/vcl/unx/gtk3/gtksys.cxx @@ -0,0 +1,339 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <gtk/gtk.h> +#include <unx/gtk/gtkdata.hxx> +#include <unx/gtk/gtkinst.hxx> +#include <unx/gtk/gtksys.hxx> +#include <unx/gtk/gtkbackend.hxx> +#include <osl/module.h> + +GtkSalSystem *GtkSalSystem::GetSingleton() +{ + static GtkSalSystem *pSingleton = new GtkSalSystem(); + return pSingleton; +} + +SalSystem *GtkInstance::CreateSalSystem() +{ + return GtkSalSystem::GetSingleton(); +} + +GtkSalSystem::GtkSalSystem() +{ + mpDisplay = gdk_display_get_default(); +#if !GTK_CHECK_VERSION(4, 0, 0) + countScreenMonitors(); +#endif + // rhbz#1285356, native look will be gtk2, which crashes + // when gtk3 is already loaded. Until there is a solution + // java-side force look and feel to something that doesn't + // crash when we are using gtk3 + setenv("STOC_FORCE_SYSTEM_LAF", "true", 1); +} + +GtkSalSystem::~GtkSalSystem() +{ +} + +namespace +{ + +struct GdkRectangleCoincidentLess +{ + // fdo#78799 - detect and elide overlaying monitors of different sizes + bool operator()(GdkRectangle const& rLeft, GdkRectangle const& rRight) + { + return + rLeft.x < rRight.x + || rLeft.y < rRight.y + ; + } +}; +struct GdkRectangleCoincident +{ + // fdo#78799 - detect and elide overlaying monitors of different sizes + bool operator()(GdkRectangle const& rLeft, GdkRectangle const& rRight) + { + return + rLeft.x == rRight.x + && rLeft.y == rRight.y + ; + } +}; + +} + +#if !GTK_CHECK_VERSION(4, 0, 0) +/** + * GtkSalSystem::countScreenMonitors() + * + * This method builds the vector which allows us to map from VCL's + * idea of linear integer ScreenNumber to gtk+'s rather more + * complicated screen + monitor concept. + */ +void +GtkSalSystem::countScreenMonitors() +{ + maScreenMonitors.clear(); + for (gint i = 0; i < gdk_display_get_n_screens(mpDisplay); i++) + { + GdkScreen* const pScreen(gdk_display_get_screen(mpDisplay, i)); + gint nMonitors(pScreen ? gdk_screen_get_n_monitors(pScreen) : 0); + if (nMonitors > 1) + { + std::vector<GdkRectangle> aGeometries; + aGeometries.reserve(nMonitors); + for (gint j(0); j != nMonitors; ++j) + { + GdkRectangle aGeometry; + gdk_screen_get_monitor_geometry(pScreen, j, &aGeometry); + aGeometries.push_back(aGeometry); + } + std::sort(aGeometries.begin(), aGeometries.end(), + GdkRectangleCoincidentLess()); + const std::vector<GdkRectangle>::iterator aUniqueEnd( + std::unique(aGeometries.begin(), aGeometries.end(), + GdkRectangleCoincident())); + nMonitors = std::distance(aGeometries.begin(), aUniqueEnd); + } + maScreenMonitors.emplace_back(pScreen, nMonitors); + } +} +#endif + +SalX11Screen +GtkSalSystem::getXScreenFromDisplayScreen(unsigned int nScreen) +{ + if (!DLSYM_GDK_IS_X11_DISPLAY(mpDisplay)) + return SalX11Screen (0); + +#if GTK_CHECK_VERSION(4, 0, 0) + GdkX11Screen *pScreen = gdk_x11_display_get_screen(mpDisplay); + (void)nScreen; +#else + gint nMonitor; + GdkScreen *pScreen = getScreenMonitorFromIdx (nScreen, nMonitor); + if (!pScreen) + return SalX11Screen (0); +#endif + return SalX11Screen(gdk_x11_screen_get_screen_number(pScreen)); +} + +#if !GTK_CHECK_VERSION(4, 0, 0) +GdkScreen * +GtkSalSystem::getScreenMonitorFromIdx (int nIdx, gint &nMonitor) +{ + GdkScreen *pScreen = nullptr; + for (auto const& screenMonitor : maScreenMonitors) + { + pScreen = screenMonitor.first; + if (!pScreen) + break; + if (nIdx >= screenMonitor.second) + nIdx -= screenMonitor.second; + else + break; + } + nMonitor = nIdx; + + // handle invalid monitor indexes as non-existent screens + if (nMonitor < 0 || (pScreen && nMonitor >= gdk_screen_get_n_monitors (pScreen))) + pScreen = nullptr; + + return pScreen; +} + +int +GtkSalSystem::getScreenIdxFromPtr (GdkScreen *pScreen) +{ + int nIdx = 0; + for (auto const& screenMonitor : maScreenMonitors) + { + if (screenMonitor.first == pScreen) + return nIdx; + nIdx += screenMonitor.second; + } + g_warning ("failed to find screen %p", pScreen); + return 0; +} + +int GtkSalSystem::getScreenMonitorIdx (GdkScreen *pScreen, + int nX, int nY) +{ + // TODO: this will fail horribly for exotic combinations like two + // monitors in mirror mode and one extra. Hopefully such + // abominations are not used (or, even better, not possible) in + // practice .-) + return getScreenIdxFromPtr (pScreen) + + gdk_screen_get_monitor_at_point (pScreen, nX, nY); +} +#endif + +unsigned int GtkSalSystem::GetDisplayScreenCount() +{ +#if GTK_CHECK_VERSION(4, 0, 0) + return g_list_model_get_n_items(gdk_display_get_monitors(mpDisplay)); +#else + gint nMonitor; + (void)getScreenMonitorFromIdx (G_MAXINT, nMonitor); + return G_MAXINT - nMonitor; +#endif +} + +bool GtkSalSystem::IsUnifiedDisplay() +{ +#if !GTK_CHECK_VERSION(4, 0, 0) + return gdk_display_get_n_screens (mpDisplay) == 1; +#else + return true; +#endif +} + +unsigned int GtkSalSystem::GetDisplayBuiltInScreen() +{ +#if GTK_CHECK_VERSION(4, 0, 0) +#if defined(GDK_WINDOWING_X11) + if (DLSYM_GDK_IS_X11_DISPLAY(mpDisplay)) + { + GdkMonitor* pPrimary = gdk_x11_display_get_primary_monitor(mpDisplay); + GListModel* pList = gdk_display_get_monitors(mpDisplay); + int nIndex = 0; + while (gpointer pElem = g_list_model_get_item(pList, nIndex)) + { + if (pElem == pPrimary) + return nIndex; + ++nIndex; + } + } +#endif + // nothing for wayland ?, hope for the best that its at index 0 + return 0; +#else // !GTK_CHECK_VERSION(4, 0, 0) + GdkScreen *pDefault = gdk_display_get_default_screen (mpDisplay); + int idx = getScreenIdxFromPtr (pDefault); + return idx + gdk_screen_get_primary_monitor(pDefault); +#endif +} + +tools::Rectangle GtkSalSystem::GetDisplayScreenPosSizePixel(unsigned int nScreen) +{ + GdkRectangle aRect; +#if GTK_CHECK_VERSION(4, 0, 0) + GListModel* pList = gdk_display_get_monitors(mpDisplay); + GdkMonitor* pMonitor = static_cast<GdkMonitor*>(g_list_model_get_item(pList, nScreen)); + if (!pMonitor) + return tools::Rectangle(); + gdk_monitor_get_geometry(pMonitor, &aRect); +#else + gint nMonitor; + GdkScreen *pScreen; + pScreen = getScreenMonitorFromIdx (nScreen, nMonitor); + if (!pScreen) + return tools::Rectangle(); + gdk_screen_get_monitor_geometry (pScreen, nMonitor, &aRect); +#endif + return tools::Rectangle (Point(aRect.x, aRect.y), Size(aRect.width, aRect.height)); +} + +// convert ~ to indicate mnemonic to '_' +static OString MapToGtkAccelerator(const OUString &rStr) +{ + return OUStringToOString(rStr.replaceFirst("~", "_"), RTL_TEXTENCODING_UTF8); +} + +#if GTK_CHECK_VERSION(4, 0, 0) + +namespace +{ + struct DialogLoop + { + GMainLoop* pLoop = nullptr; + gint nResponseId = GTK_RESPONSE_NONE; + gulong nSignalResponseId = 0; + gulong nSignalCloseRequestId= 0; + + static gboolean DialogClose(GtkWindow* pDialog, gpointer /*data*/) + { + gtk_dialog_response(GTK_DIALOG(pDialog), GTK_RESPONSE_CANCEL); + return true; + } + + static void DialogResponse(GtkDialog* pDialog, gint nResponseId, gpointer data) + { + DialogLoop* pDialogLoop = static_cast<DialogLoop*>(data); + g_signal_handler_disconnect(pDialog, pDialogLoop->nSignalResponseId); + g_signal_handler_disconnect(pDialog, pDialogLoop->nSignalCloseRequestId); + pDialogLoop->nResponseId = nResponseId; + g_main_loop_quit(pDialogLoop->pLoop); + } + + int run(GtkDialog *pDialog) + { + nSignalResponseId = g_signal_connect(pDialog, "response", G_CALLBACK(DialogResponse), this); + nSignalCloseRequestId = g_signal_connect(pDialog, "close-request", G_CALLBACK(DialogClose), this); + gtk_window_present(GTK_WINDOW(pDialog)); + pLoop = g_main_loop_new(nullptr, false); + main_loop_run(pLoop); + g_main_loop_unref(pLoop); + return nResponseId; + } + + }; +} + +gint gtk_dialog_run(GtkDialog* pDialog) +{ + DialogLoop aDialogLoop; + return aDialogLoop.run(pDialog); +} + +#endif + +int GtkSalSystem::ShowNativeDialog (const OUString& rTitle, const OUString& rMessage, + const std::vector< OUString >& rButtonNames) +{ + OString aTitle (OUStringToOString (rTitle, RTL_TEXTENCODING_UTF8)); + OString aMessage (OUStringToOString (rMessage, RTL_TEXTENCODING_UTF8)); + + GtkDialog *pDialog = GTK_DIALOG ( + g_object_new (GTK_TYPE_MESSAGE_DIALOG, + "title", aTitle.getStr(), + "message-type", int(GTK_MESSAGE_WARNING), + "text", aMessage.getStr(), + nullptr)); + int nButton = 0; + for (auto const& buttonName : rButtonNames) + gtk_dialog_add_button (pDialog, MapToGtkAccelerator(buttonName).getStr(), nButton++); + gtk_dialog_set_default_response (pDialog, 0/*nDefaultButton*/); + + nButton = gtk_dialog_run (pDialog); + if (nButton < 0) + nButton = -1; + +#if !GTK_CHECK_VERSION(4, 0, 0) + gtk_widget_destroy(GTK_WIDGET(pDialog)); +#else + gtk_window_destroy(GTK_WINDOW(pDialog)); +#endif + + return nButton; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |