Kaydet (Commit) 788716ce authored tarafından Michael Weghorn's avatar Michael Weghorn

tdf#120261 gtk3_kde5: Leave event handling on gtk3 side to gtk

This introduces a new thread that takes care of handling the IPC
command and result for executing the file picker
('Commands::Execute'), which is the only command in the file
picker that may block for a longer time until it receives its
result from the kde5 side (namely, when the dialog has been
closed).

While the file dialog is being executed, activate a dummy GTK dialog
that also takes care of handling events in its main loop as long
as the file dialog is shown. The dummy dialog is closed together
with the KDE file dialog.
(Since the actual KDE file dialog is run as a separate process,
this one is mostly "transparent" to the soffice process from the
point of view of a dialog.)

This allows dropping the custom event processing previously done
in 'Gtk3KDE5FilePickerIpc::readResponse()' that had the potential
to cause all kinds of problems, e.g. when another event related to
the file picker was triggered from a Java process via UNO.

Change-Id: I3d663253f09320f7a8e0d9ec32a8fd6ec191c189
Reviewed-on: https://gerrit.libreoffice.org/61253
Tested-by: Jenkins
Reviewed-by: 's avatarMichael Weghorn <m.weghorn@posteo.de>
üst 3b36855e
......@@ -106,6 +106,17 @@ OUString getResString(const char* pResId)
return VclResId(pResId);
}
// handles the IPC commands for dialog execution and ends the dummy Gtk dialog once the IPC response is there
void handleIpcForExecute(Gtk3KDE5FilePickerIpc* pFilePickerIpc, GtkWidget* pDummyDialog,
bool* bResult)
{
auto id = pFilePickerIpc->sendCommand(Commands::Execute);
pFilePickerIpc->readResponse(id, *bResult);
// end the dummy dialog
gtk_widget_hide(pDummyDialog);
}
// Gtk3KDE5FilePicker
Gtk3KDE5FilePickerIpc::Gtk3KDE5FilePickerIpc()
......@@ -143,9 +154,29 @@ sal_Int16 Gtk3KDE5FilePickerIpc::execute()
{
auto restoreMainWindow = blockMainWindow();
auto id = sendCommand(Commands::Execute);
// dummy gtk dialog that will take care of processing events,
// not meant to be actually seen by user
GtkWidget* pDummyDialog = gtk_dialog_new();
bool accepted = false;
readResponse(id, accepted);
// send IPC command and read response in a separate thread
std::thread aIpcHandler(&handleIpcForExecute, this, pDummyDialog, &accepted);
// make dummy dialog not to be seen by user
gtk_window_set_decorated(GTK_WINDOW(pDummyDialog), false);
gtk_window_set_default_size(GTK_WINDOW(pDummyDialog), 0, 0);
gtk_window_set_accept_focus(GTK_WINDOW(pDummyDialog), false);
// gtk_widget_set_opacity() only has the desired effect when widget is already shown
gtk_widget_show(pDummyDialog);
gtk_widget_set_opacity(pDummyDialog, 0);
// run dialog, leaving event processing to GTK
// dialog will be closed by the separate 'aIpcHandler' thread once the IPC response is there
gtk_dialog_run(GTK_DIALOG(pDummyDialog));
aIpcHandler.join();
gtk_widget_destroy(pDummyDialog);
if (restoreMainWindow)
restoreMainWindow();
......@@ -204,14 +235,6 @@ std::function<void()> Gtk3KDE5FilePickerIpc::blockMainWindow()
};
}
void Gtk3KDE5FilePickerIpc::await(const std::future<void>& future)
{
while (future.wait_for(std::chrono::milliseconds(1)) != std::future_status::ready)
{
GetGtkSalData()->Yield(false, true);
}
}
void Gtk3KDE5FilePickerIpc::writeResponseLine(const std::string& line)
{
sal_uInt64 bytesWritten = 0;
......
......@@ -30,7 +30,6 @@
#include "filepicker_ipc_commands.hxx"
#include <functional>
#include <future>
#include <mutex>
#include <thread>
#include <sstream>
......@@ -105,43 +104,37 @@ public:
template <typename... Args> void readResponse(uint64_t id, Args&... args)
{
// read synchronously from a background thread and run the eventloop until the value becomes available
// this allows us to keep the GUI responsive and also enables access to the LO clipboard
ArgsReader<Args...> argsReader(args...);
await(std::async(std::launch::async, [&]() {
while (true)
while (true)
{
// only let one thread read at any given time
std::lock_guard<std::mutex> lock(m_mutex);
// check if we need to read (and potentially wait) a response ID
if (m_incomingResponse == 0)
{
m_responseStream.clear();
m_responseStream.str(readResponseLine());
readIpcArgs(m_responseStream, m_incomingResponse);
}
if (m_incomingResponse == id)
{
// only let one thread read at any given time
std::lock_guard<std::mutex> lock(m_mutex);
// check if we need to read (and potentially wait) a response ID
if (m_incomingResponse == 0)
{
m_responseStream.clear();
m_responseStream.str(readResponseLine());
readIpcArgs(m_responseStream, m_incomingResponse);
}
if (m_incomingResponse == id)
{
// the response we are waiting for came in
argsReader(m_responseStream);
m_incomingResponse = 0;
break;
}
else
{
// the next response answers some other request, yield
std::this_thread::yield();
}
// the response we are waiting for came in
argsReader(m_responseStream);
m_incomingResponse = 0;
break;
}
}));
else
{
// the next response answers some other request, yield
std::this_thread::yield();
}
}
}
private:
std::function<void()> blockMainWindow();
static void await(const std::future<void>& future);
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment