Commit be6550cc authored by Stepan Salenikovich's avatar Stepan Salenikovich Committed by Guillaume Roguez

gnome: implement dbus error interface

DBus errors usually indicate that dring has crashed or been killed.
This handler shows a warning dialog and gives dring 2.5s to restart.
If a DBus connection is not re-established, an error dialog is show and
the client quits.

This implementation prevents the client from suddenly crashing when
dring is killed due to the exception thrown by the LRC
implementation of the interface.

Patch adds updated translation template.

Issue: #79655
Change-Id: Ie7679a11ca357d68d8b87a3925abe4598d36f02c
parent 1429b868
......@@ -240,6 +240,8 @@ SET( SRC_FILES
src/choosecontactview.cpp
src/editcontactview.h
src/editcontactview.cpp
src/native/dbuserrorhandler.h
src/native/dbuserrorhandler.cpp
)
# compile glib resource files to c code
......
/*
* Copyright (C) 2015 Savoir-faire Linux Inc.
* Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Additional permission under GNU GPL version 3 section 7:
*/
#include "dbuserrorhandler.h"
#include <glib/gi18n.h>
#include <callmodel.h>
#include <globalinstances.h>
#include "../ring_client.h"
namespace Interfaces {
static GtkWidget*
dring_crash_dialog()
{
GtkWidget *dialog = gtk_dialog_new();
gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
gtk_window_set_decorated(GTK_WINDOW(dialog), FALSE);
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
gtk_window_set_title(GTK_WINDOW(dialog), C_("Name of error window (dialog)","Ring Error"));
/* get the main window */
if (auto app = g_application_get_default()) {
auto win = ring_client_get_main_window(RING_CLIENT(app));
if (win) {
gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(win));
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
} else {
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
}
} else {
g_warning("no default GApplication exists");
}
GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
gtk_box_set_spacing(GTK_BOX(content_area), 10);
gtk_widget_set_size_request(content_area, 250, -1);
gtk_widget_set_margin_top(content_area, 25);
auto message = gtk_label_new(
_("Trying to reconnect to the Ring daemon (dring)...")
);
gtk_box_pack_start(GTK_BOX(content_area), message, FALSE, TRUE, 0);
GtkWidget *spinner = gtk_spinner_new();
gtk_spinner_start(GTK_SPINNER(spinner));
gtk_box_pack_start(GTK_BOX(content_area), spinner, FALSE, TRUE, 0);
gtk_widget_show_all(content_area);
return dialog;
}
static GtkWidget*
ring_quitting_dialog()
{
/* get the main window */
GtkWindow *win = NULL;
if (auto app = g_application_get_default()) {
win = ring_client_get_main_window(RING_CLIENT(app));
} else {
g_warning("no default GApplication exists");
}
GtkWidget *dialog = gtk_message_dialog_new(
win,
(GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
_("Could not re-connect to the Ring daemon (dring).\nRing will now quit.")
);
if (win) {
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
} else {
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
}
gtk_window_set_title(GTK_WINDOW(dialog), C_("Name of error window (dialog)","Ring Error"));
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
return dialog;
}
static gboolean
check_connection_cb(GtkWidget *warning_dialog)
{
g_return_val_if_fail(GTK_IS_DIALOG(warning_dialog), G_SOURCE_REMOVE);
gtk_widget_destroy(warning_dialog);
if ((!CallModel::instance()->isConnected()) || (!CallModel::instance()->isValid())) {
g_warning("could not reconnect to the daemon");
auto quit_dialog = ring_quitting_dialog();
/* wait for the user to exit the dialog */
gtk_dialog_run(GTK_DIALOG(quit_dialog));
gtk_widget_destroy(quit_dialog);
/* quit */
if (auto app = g_application_get_default()) {
auto quit_action = G_ACTION(g_action_map_lookup_action(G_ACTION_MAP(app), "quit"));
g_action_activate(quit_action, NULL);
} else {
g_warning("no default GApplication exists");
}
} else {
/* we're done handling the error */
static_cast<DBusErrorHandler&>(GlobalInstances::dBusErrorHandler()).finishedHandlingError();
}
return G_SOURCE_REMOVE;
}
static gboolean
error_cb(G_GNUC_UNUSED gpointer user_data)
{
g_warning("dring has possibly crashed, or has been killed... will wait 2.5 seconds and try to reconnect");
auto warning_dialog = dring_crash_dialog();
gtk_window_present(GTK_WINDOW(warning_dialog));
/* allow 2.5 seconds for the daemon to restart and then see if we're re-connected */
g_timeout_add(2500, (GSourceFunc)check_connection_cb, warning_dialog);
return G_SOURCE_REMOVE;
}
void
DBusErrorHandler::connectionError(const QString& error)
{
g_warning("%s", error.toUtf8().constData());
if (!handlingError) {
handlingError = true;
/* the error may come from a different thread other than the main loop,
* we use an idle function to run events on the main loop */
g_idle_add((GSourceFunc)error_cb, NULL);
}
}
void
DBusErrorHandler::invalidInterfaceError(const QString& error)
{
g_warning("%s", error.toUtf8().constData());
if (!handlingError) {
handlingError = true;
/* the error may come from a different thread other than the main loop,
* we use an idle function to run events on the main loop */
g_idle_add((GSourceFunc)error_cb, NULL);
}
}
void
DBusErrorHandler::finishedHandlingError()
{
handlingError = false;
}
} // namespace Interfaces
/*
* Copyright (C) 2015 Savoir-faire Linux Inc.
* Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <gtk/gtk.h>
#include <interfaces/dbuserrorhandleri.h>
#include <atomic>
namespace Interfaces {
class DBusErrorHandler : public DBusErrorHandlerI {
public:
void connectionError(const QString& error) override;
void invalidInterfaceError(const QString& error) override;
void finishedHandlingError();
private:
/* keeps track if we're in the process of handling an error already, so that we don't keep
* displaying error dialogs; we use an atomic in case the errors come from multiple threads */
std::atomic_bool handlingError{false};
};
} // namespace Interfaces
......@@ -55,6 +55,7 @@
#include "dialogs.h"
#include "backends/edscontactbackend.h"
#include "native/pixbufmanipulator.h"
#include "native/dbuserrorhandler.h"
#include "ringnotify.h"
#include "config.h"
#include "utils/files.h"
......@@ -278,6 +279,7 @@ ring_client_startup(GApplication *app)
/* init delegates */
GlobalInstances::setPixmapManipulator(std::unique_ptr<Interfaces::PixbufManipulator>(new Interfaces::PixbufManipulator()));
GlobalInstances::setDBusErrorHandler(std::unique_ptr<Interfaces::DBusErrorHandler>(new Interfaces::DBusErrorHandler()));
/* make sure all RING accounts have a display name... this basically makes sure
* that all accounts created before the display name patch have a display name
......@@ -489,7 +491,7 @@ ring_client_new(int argc, char *argv[])
}
GtkWindow *
ring_client_get_main_windw(RingClient *client)
ring_client_get_main_window(RingClient *client)
{
g_return_val_if_fail(IS_RING_CLIENT(client), NULL);
RingClientPrivate *priv = RING_CLIENT_GET_PRIVATE(client);
......
......@@ -50,7 +50,7 @@ typedef struct _RingClient RingClient;
/* Public interface */
GType ring_client_get_type (void) G_GNUC_CONST;
RingClient *ring_client_new (int argc, char *argv[]);
GtkWindow *ring_client_get_main_windw(RingClient *client);
GtkWindow *ring_client_get_main_window(RingClient *client);
G_END_DECLS
......
......@@ -227,7 +227,7 @@ ring_notify_call_messages(Call *call, Media::Text *media, RingClient *client)
&Media::Text::messageReceived,
[call, client] (const QMap<QString,QString>& message) {
g_return_if_fail(call && client);
GtkWindow *main_window = ring_client_get_main_windw(client);
GtkWindow *main_window = ring_client_get_main_window(client);
if ( main_window && gtk_window_is_active(main_window)) {
/* only notify about messages not in the currently selected call */
if (CallModel::instance()->selectedCall() != call) {
......
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