bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / unx / gtk3_kde5 / gtk3_kde5_filepicker_ipc.cxx
blobf9b0da12ce9ca39626fd8cadf2b2db2ab7711844
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 <sal/config.h>
22 #include <string_view>
24 #include "gtk3_kde5_filepicker_ipc.hxx"
26 #undef Region
28 #include <system_error>
30 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
32 #include <vcl/svapp.hxx>
33 #include <vcl/sysdata.hxx>
34 #include <vcl/weld.hxx>
36 #include <osl/file.h>
37 #include <osl/process.h>
39 #include <gtk/gtk.h>
41 #include <boost/filesystem/path.hpp>
43 #include <fpicker/fpsofficeResMgr.hxx>
45 using namespace ::com::sun::star::ui::dialogs;
47 // helper functions
49 namespace
51 OUString applicationDirPath()
53 OUString applicationFilePath;
54 osl_getExecutableFile(&applicationFilePath.pData);
55 OUString applicationSystemPath;
56 osl_getSystemPathFromFileURL(applicationFilePath.pData, &applicationSystemPath.pData);
57 const auto utf8Path = applicationSystemPath.toUtf8();
58 auto ret = boost::filesystem::path(utf8Path.getStr(), utf8Path.getStr() + utf8Path.getLength());
59 ret.remove_filename();
60 return OUString::fromUtf8(std::string_view(ret.c_str()));
63 OUString findPickerExecutable()
65 const auto path = applicationDirPath();
66 const OUString app("lo_kde5filepicker");
67 OUString ret;
68 osl_searchFileURL(app.pData, path.pData, &ret.pData);
69 if (ret.isEmpty())
70 throw std::system_error(std::make_error_code(std::errc::no_such_file_or_directory),
71 "could not find lo_kde5filepicker executable");
72 return ret;
76 void readIpcArg(std::istream& stream, OUString& str)
78 const auto buffer = readIpcStringArg(stream);
79 str = OUString::fromUtf8(std::string_view(buffer.data(), buffer.size()));
82 void readIpcArg(std::istream& stream, css::uno::Sequence<OUString>& seq)
84 uint32_t numFiles = 0;
85 stream >> numFiles;
86 stream.ignore(); // skip space;
87 seq.realloc(numFiles);
88 OUString* pSeq = seq.getArray();
89 for (size_t i = 0; i < numFiles; ++i)
91 readIpcArg(stream, pSeq[i]);
95 void sendIpcArg(std::ostream& stream, const OUString& string)
97 const auto utf8 = string.toUtf8();
98 sendIpcStringArg(stream, utf8.getLength(), utf8.getStr());
101 OUString getResString(TranslateId pResId)
103 if (!pResId)
104 return {};
106 return FpsResId(pResId);
109 // handles the IPC commands for dialog execution and ends the dummy Gtk dialog once the IPC response is there
110 static void handleIpcForExecute(Gtk3KDE5FilePickerIpc* pFilePickerIpc, GtkWidget* pDummyDialog,
111 bool* bResult)
113 auto id = pFilePickerIpc->sendCommand(Commands::Execute);
114 pFilePickerIpc->readResponse(id, *bResult);
116 // end the dummy dialog
117 gtk_widget_hide(pDummyDialog);
120 // Gtk3KDE5FilePicker
122 Gtk3KDE5FilePickerIpc::Gtk3KDE5FilePickerIpc()
124 const auto exe = findPickerExecutable();
125 oslProcessError result;
126 oslSecurity pSecurity = osl_getCurrentSecurity();
127 result = osl_executeProcess_WithRedirectedIO(exe.pData, nullptr, 0, osl_Process_NORMAL,
128 pSecurity, nullptr, nullptr, 0, &m_process,
129 &m_inputWrite, &m_outputRead, nullptr);
130 osl_freeSecurityHandle(pSecurity);
131 if (result != osl_Process_E_None)
132 throw std::system_error(std::make_error_code(std::errc::no_such_process),
133 "could not start lo_kde5filepicker executable");
136 Gtk3KDE5FilePickerIpc::~Gtk3KDE5FilePickerIpc()
138 if (!m_process)
139 return;
141 sendCommand(Commands::Quit);
142 osl_joinProcess(m_process);
144 if (m_inputWrite)
145 osl_closeFile(m_inputWrite);
146 if (m_outputRead)
147 osl_closeFile(m_outputRead);
148 osl_freeProcessHandle(m_process);
151 sal_Int16 Gtk3KDE5FilePickerIpc::execute()
153 auto restoreMainWindow = blockMainWindow();
155 // dummy gtk dialog that will take care of processing events,
156 // not meant to be actually seen by user
157 GtkWidget* pDummyDialog = gtk_dialog_new();
159 bool accepted = false;
161 // send IPC command and read response in a separate thread
162 std::thread aIpcHandler(&handleIpcForExecute, this, pDummyDialog, &accepted);
164 // make dummy dialog not to be seen by user
165 gtk_window_set_decorated(GTK_WINDOW(pDummyDialog), false);
166 gtk_window_set_default_size(GTK_WINDOW(pDummyDialog), 0, 0);
167 gtk_window_set_accept_focus(GTK_WINDOW(pDummyDialog), false);
168 // gtk_widget_set_opacity() only has the desired effect when widget is already shown
169 gtk_widget_show(pDummyDialog);
170 gtk_widget_set_opacity(pDummyDialog, 0);
171 // run dialog, leaving event processing to GTK
172 // dialog will be closed by the separate 'aIpcHandler' thread once the IPC response is there
173 gtk_dialog_run(GTK_DIALOG(pDummyDialog));
175 aIpcHandler.join();
177 gtk_widget_destroy(pDummyDialog);
179 if (restoreMainWindow)
180 restoreMainWindow();
182 return accepted ? ExecutableDialogResults::OK : ExecutableDialogResults::CANCEL;
185 static gboolean ignoreDeleteEvent(GtkWidget* /*widget*/, GdkEvent* /*event*/,
186 gpointer /*user_data*/)
188 return true;
191 std::function<void()> Gtk3KDE5FilePickerIpc::blockMainWindow()
193 weld::Window* pParentWin = Application::GetDefDialogParent();
194 if (!pParentWin)
195 return {};
197 const SystemEnvData aSysData = pParentWin->get_system_data();
198 auto* pMainWindow = static_cast<GtkWidget*>(aSysData.pWidget);
199 if (!pMainWindow)
200 return {};
202 sendCommand(Commands::SetWinId, aSysData.GetWindowHandle(aSysData.pSalFrame));
204 SolarMutexGuard guard;
205 auto deleteEventSignalId = g_signal_lookup("delete_event", gtk_widget_get_type());
207 // disable the mainwindow
208 gtk_widget_set_sensitive(pMainWindow, false);
210 // block the GtkSalFrame delete_event handler
211 auto blockedHandler = g_signal_handler_find(
212 pMainWindow, static_cast<GSignalMatchType>(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA),
213 deleteEventSignalId, 0, nullptr, nullptr, aSysData.pSalFrame);
214 g_signal_handler_block(pMainWindow, blockedHandler);
216 // prevent the window from being closed
217 auto ignoreDeleteEventHandler
218 = g_signal_connect(pMainWindow, "delete_event", G_CALLBACK(ignoreDeleteEvent), nullptr);
220 return [pMainWindow, ignoreDeleteEventHandler, blockedHandler] {
221 SolarMutexGuard cleanupGuard;
222 // re-enable window
223 gtk_widget_set_sensitive(pMainWindow, true);
225 // allow it to be closed again
226 g_signal_handler_disconnect(pMainWindow, ignoreDeleteEventHandler);
228 // unblock the GtkSalFrame handler
229 g_signal_handler_unblock(pMainWindow, blockedHandler);
233 void Gtk3KDE5FilePickerIpc::writeResponseLine(const std::string& line)
235 sal_uInt64 bytesWritten = 0;
236 osl_writeFile(m_inputWrite, line.c_str(), line.size(), &bytesWritten);
239 std::string Gtk3KDE5FilePickerIpc::readResponseLine()
241 if (!m_responseBuffer.empty()) // check whether we have a line in our buffer
243 std::size_t it = m_responseBuffer.find('\n');
244 if (it != std::string::npos)
246 auto ret = m_responseBuffer.substr(0, it);
247 m_responseBuffer.erase(0, it + 1);
248 return ret;
252 const sal_uInt64 BUF_SIZE = 1024;
253 char buffer[BUF_SIZE];
254 while (true)
256 sal_uInt64 bytesRead = 0;
257 auto err = osl_readFile(m_outputRead, buffer, BUF_SIZE, &bytesRead);
258 auto it = std::find(buffer, buffer + bytesRead, '\n');
259 if (it != buffer + bytesRead) // check whether the chunk we read contains an EOL
261 // if so, append that part to the buffer and return it
262 std::string ret = m_responseBuffer.append(buffer, it);
263 // but keep anything else we may have read in our buffer
264 ++it;
265 m_responseBuffer.assign(it, buffer + bytesRead);
266 return ret;
268 // otherwise append everything we read to the buffer and try again
269 m_responseBuffer.append(buffer, bytesRead);
271 if (err != osl_File_E_None && err != osl_File_E_AGAIN)
272 break;
274 return {};
277 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */