add display chat manager, notifications support outside game_display
The functionality of tracking observers and displaying chat messages is moved to a manager class, which the gui owns. The functionality of displaying notifications is similarly moved out of the game_display and to a private namespace. (Static singleton pattern seems okay here since there really won't need to be more than one of these for a single application, it seems.)
This commit is contained in:
parent
b0e407ac66
commit
cb5a9a0d28
25 changed files with 612 additions and 447 deletions
|
@ -229,6 +229,8 @@
|
|||
<Unit filename="..\..\src\dialogs.hpp" />
|
||||
<Unit filename="..\..\src\display.cpp" />
|
||||
<Unit filename="..\..\src\display.hpp" />
|
||||
<Unit filename="..\..\src\display_chat_manager.cpp" />
|
||||
<Unit filename="..\..\src\display_chat_manager.hpp" />
|
||||
<Unit filename="..\..\src\display_context.cpp" />
|
||||
<Unit filename="..\..\src\display_context.hpp" />
|
||||
<Unit filename="..\..\src\editor\action\action.cpp" />
|
||||
|
@ -860,6 +862,8 @@
|
|||
<Unit filename="..\..\src\network_asio.hpp" />
|
||||
<Unit filename="..\..\src\network_worker.cpp" />
|
||||
<Unit filename="..\..\src\network_worker.hpp" />
|
||||
<Unit filename="..\..\src\notifications.cpp" />
|
||||
<Unit filename="..\..\src\notifications.hpp" />
|
||||
<Unit filename="..\..\src\overlay.hpp" />
|
||||
<Unit filename="..\..\src\pathfind\astarsearch.cpp" />
|
||||
<Unit filename="..\..\src\pathfind\pathfind.cpp" />
|
||||
|
|
|
@ -20108,6 +20108,14 @@
|
|||
RelativePath="..\..\src\display.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\display_chat_manager.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\display_chat_manager.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\display_context.cpp"
|
||||
>
|
||||
|
@ -20696,6 +20704,14 @@
|
|||
RelativePath="..\..\src\network_worker.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\notifications.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\notifications.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\overlay.hpp"
|
||||
>
|
||||
|
|
|
@ -700,6 +700,7 @@ set(wesnoth-main_SRC
|
|||
controller_base.cpp
|
||||
desktop_util.cpp
|
||||
dialogs.cpp
|
||||
display_chat_manager.cpp
|
||||
editor/action/action.cpp
|
||||
editor/action/action_item.cpp
|
||||
editor/action/action_label.cpp
|
||||
|
@ -843,6 +844,7 @@ set(wesnoth-main_SRC
|
|||
multiplayer_ui.cpp
|
||||
multiplayer_wait.cpp
|
||||
network_asio.cpp
|
||||
notifications.cpp
|
||||
pathfind/pathfind.cpp
|
||||
pathfind/teleport.cpp
|
||||
persist_context.cpp
|
||||
|
|
|
@ -233,6 +233,7 @@ wesnoth_sources = Split("""
|
|||
controller_base.cpp
|
||||
desktop_util.cpp
|
||||
dialogs.cpp
|
||||
display_chat_manager.cpp
|
||||
editor/action/action.cpp
|
||||
editor/action/action_unit.cpp
|
||||
editor/action/action_label.cpp
|
||||
|
@ -477,6 +478,7 @@ wesnoth_sources = Split("""
|
|||
multiplayer_ui.cpp
|
||||
multiplayer_wait.cpp
|
||||
network_asio.cpp
|
||||
notifications.cpp
|
||||
pathfind/pathfind.cpp
|
||||
pathfind/teleport.cpp
|
||||
persist_context.cpp
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include "chat_events.hpp" // for chat_handler, etc
|
||||
#include "config.hpp" // for config, etc
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "game_board.hpp" // for game_board
|
||||
#include "game_config.hpp" // for debug
|
||||
#include "game_display.hpp" // for game_display
|
||||
|
@ -353,7 +354,7 @@ const team& readonly_context_impl::current_team() const
|
|||
void readonly_context_impl::log_message(const std::string& msg)
|
||||
{
|
||||
if(game_config::debug) {
|
||||
resources::screen->add_chat_message(time(NULL), "ai", get_side(), msg,
|
||||
resources::screen->get_chat_manager().add_chat_message(time(NULL), "ai", get_side(), msg,
|
||||
events::chat_handler::MESSAGE_PUBLIC, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "../../callable_objects.hpp" // for unit_callable, etc
|
||||
#include "../../chat_events.hpp" // for chat_handler, etc
|
||||
#include "../../display_chat_manager.hpp"
|
||||
#include "../../formula_function.hpp" // for formula_expression
|
||||
#include "../../game_board.hpp" // for game_board
|
||||
#include "../../game_display.hpp" // for game_display
|
||||
|
@ -127,7 +128,7 @@ void formula_ai::handle_exception(game_logic::formula_error& e, const std::strin
|
|||
|
||||
void formula_ai::display_message(const std::string& msg) const
|
||||
{
|
||||
resources::screen->add_chat_message(time(NULL), "fai", get_side(), msg,
|
||||
resources::screen->get_chat_manager().add_chat_message(time(NULL), "fai", get_side(), msg,
|
||||
events::chat_handler::MESSAGE_PUBLIC, false);
|
||||
|
||||
}
|
||||
|
|
209
src/display_chat_manager.cpp
Normal file
209
src/display_chat_manager.cpp
Normal file
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
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 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "global.hpp"
|
||||
|
||||
#include "display.hpp"
|
||||
#include "font.hpp"
|
||||
#include "game_board.hpp" // <-- only needed for is_observer()
|
||||
#include "game_preferences.hpp"
|
||||
#include "log.hpp"
|
||||
#include "marked-up_text.hpp"
|
||||
#include "notifications.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_video.h"
|
||||
|
||||
static lg::log_domain log_engine("engine");
|
||||
#define ERR_NG LOG_STREAM(err, log_engine)
|
||||
|
||||
using boost::uint32_t;
|
||||
|
||||
namespace {
|
||||
const int chat_message_border = 5;
|
||||
const int chat_message_x = 10;
|
||||
const SDL_Color chat_message_color = {255,255,255,255};
|
||||
const SDL_Color chat_message_bg = {0,0,0,140};
|
||||
}
|
||||
|
||||
display_chat_manager::chat_message::chat_message(int speaker, int h)
|
||||
: speaker_handle(speaker), handle(h), created_at(SDL_GetTicks())
|
||||
{}
|
||||
|
||||
|
||||
void display_chat_manager::add_chat_message(const time_t& time, const std::string& speaker,
|
||||
int side, const std::string& message, events::chat_handler::MESSAGE_TYPE type,
|
||||
bool bell)
|
||||
{
|
||||
const bool whisper = speaker.find("whisper: ") == 0;
|
||||
std::string sender = speaker;
|
||||
if (whisper) {
|
||||
sender.assign(speaker, 9, speaker.size());
|
||||
}
|
||||
if (!preferences::parse_should_show_lobby_join(sender, message)) return;
|
||||
if (preferences::is_ignored(sender)) return;
|
||||
|
||||
preferences::parse_admin_authentication(sender, message);
|
||||
|
||||
bool is_observer = false;
|
||||
{ //TODO: Clean this block up somehow
|
||||
|
||||
const game_board * board = dynamic_cast<const game_board*>(&my_disp_.get_disp_context());
|
||||
|
||||
if (board) {
|
||||
is_observer = board->is_observer();
|
||||
}
|
||||
}
|
||||
|
||||
if (bell) {
|
||||
if ((type == events::chat_handler::MESSAGE_PRIVATE && (!is_observer || whisper))
|
||||
|| utils::word_match(message, preferences::login())) {
|
||||
sound::play_UI_sound(game_config::sounds::receive_message_highlight);
|
||||
} else if (preferences::is_friend(sender)) {
|
||||
sound::play_UI_sound(game_config::sounds::receive_message_friend);
|
||||
} else if (sender == "server") {
|
||||
sound::play_UI_sound(game_config::sounds::receive_message_server);
|
||||
} else {
|
||||
sound::play_UI_sound(game_config::sounds::receive_message);
|
||||
}
|
||||
}
|
||||
|
||||
bool action = false;
|
||||
|
||||
std::string msg;
|
||||
|
||||
if (message.find("/me ") == 0) {
|
||||
msg.assign(message, 4, message.size());
|
||||
action = true;
|
||||
} else {
|
||||
msg += message;
|
||||
}
|
||||
|
||||
try {
|
||||
// We've had a joker who send an invalid utf-8 message to crash clients
|
||||
// so now catch the exception and ignore the message.
|
||||
msg = font::word_wrap_text(msg,font::SIZE_SMALL,my_disp_.map_outside_area().w*3/4);
|
||||
} catch (utf8::invalid_utf8_exception&) {
|
||||
ERR_NG << "Invalid utf-8 found, chat message is ignored." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
int ypos = chat_message_x;
|
||||
for(std::vector<chat_message>::const_iterator m = chat_messages_.begin(); m != chat_messages_.end(); ++m) {
|
||||
ypos += std::max(font::get_floating_label_rect(m->handle).h,
|
||||
font::get_floating_label_rect(m->speaker_handle).h);
|
||||
}
|
||||
SDL_Color speaker_color = {255,255,255,255};
|
||||
if(side >= 1) {
|
||||
speaker_color = int_to_color(team::get_side_color_range(side).mid());
|
||||
}
|
||||
|
||||
SDL_Color message_color = chat_message_color;
|
||||
std::stringstream str;
|
||||
std::stringstream message_str;
|
||||
|
||||
if(type == events::chat_handler::MESSAGE_PUBLIC) {
|
||||
if(action) {
|
||||
str << "<" << speaker << " " << msg << ">";
|
||||
message_color = speaker_color;
|
||||
message_str << " ";
|
||||
} else {
|
||||
if (!speaker.empty())
|
||||
str << "<" << speaker << ">";
|
||||
message_str << msg;
|
||||
}
|
||||
} else {
|
||||
if(action) {
|
||||
str << "*" << speaker << " " << msg << "*";
|
||||
message_color = speaker_color;
|
||||
message_str << " ";
|
||||
} else {
|
||||
if (!speaker.empty())
|
||||
str << "*" << speaker << "*";
|
||||
message_str << msg;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepend message with timestamp.
|
||||
std::stringstream message_complete;
|
||||
message_complete << preferences::get_chat_timestamp(time) << str.str();
|
||||
|
||||
const SDL_Rect rect = my_disp_.map_outside_area();
|
||||
|
||||
font::floating_label spk_flabel(message_complete.str());
|
||||
spk_flabel.set_font_size(font::SIZE_SMALL);
|
||||
spk_flabel.set_color(speaker_color);
|
||||
spk_flabel.set_position(rect.x + chat_message_x, rect.y + ypos);
|
||||
spk_flabel.set_clip_rect(rect);
|
||||
spk_flabel.set_alignment(font::LEFT_ALIGN);
|
||||
spk_flabel.set_bg_color(chat_message_bg);
|
||||
spk_flabel.set_border_size(chat_message_border);
|
||||
spk_flabel.use_markup(false);
|
||||
|
||||
int speaker_handle = font::add_floating_label(spk_flabel);
|
||||
|
||||
font::floating_label msg_flabel(message_str.str());
|
||||
msg_flabel.set_font_size(font::SIZE_SMALL);
|
||||
msg_flabel.set_color(message_color);
|
||||
msg_flabel.set_position(rect.x + chat_message_x + font::get_floating_label_rect(speaker_handle).w,
|
||||
rect.y + ypos);
|
||||
msg_flabel.set_clip_rect(rect);
|
||||
msg_flabel.set_alignment(font::LEFT_ALIGN);
|
||||
msg_flabel.set_bg_color(chat_message_bg);
|
||||
msg_flabel.set_border_size(chat_message_border);
|
||||
msg_flabel.use_markup(false);
|
||||
|
||||
int message_handle = font::add_floating_label(msg_flabel);
|
||||
|
||||
// Send system notification if appropriate.
|
||||
notifications::send_notification(speaker, message);
|
||||
|
||||
chat_messages_.push_back(chat_message(speaker_handle,message_handle));
|
||||
|
||||
prune_chat_messages();
|
||||
}
|
||||
|
||||
void display_chat_manager::prune_chat_messages(bool remove_all)
|
||||
{
|
||||
const unsigned message_aging = preferences::chat_message_aging();
|
||||
const unsigned message_ttl = remove_all ? 0 : message_aging * 60 * 1000;
|
||||
const unsigned max_chat_messages = preferences::chat_lines();
|
||||
int movement = 0;
|
||||
|
||||
if(message_aging != 0 || remove_all || chat_messages_.size() > max_chat_messages) {
|
||||
while (!chat_messages_.empty() &&
|
||||
(chat_messages_.front().created_at + message_ttl < SDL_GetTicks() ||
|
||||
chat_messages_.size() > max_chat_messages))
|
||||
{
|
||||
const chat_message &old = chat_messages_.front();
|
||||
movement += font::get_floating_label_rect(old.handle).h;
|
||||
font::remove_floating_label(old.speaker_handle);
|
||||
font::remove_floating_label(old.handle);
|
||||
chat_messages_.erase(chat_messages_.begin());
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const chat_message &cm, chat_messages_) {
|
||||
font::move_floating_label(cm.speaker_handle, 0, - movement);
|
||||
font::move_floating_label(cm.handle, 0, - movement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
60
src/display_chat_manager.hpp
Normal file
60
src/display_chat_manager.hpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
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 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef INCL_DISPLAY_CHAT_MGR_HPP_
|
||||
#define INCL_DISPLAY_CHAT_MGR_HPP_
|
||||
|
||||
#include "chat_events.hpp"
|
||||
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <ctime>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class display;
|
||||
|
||||
class display_chat_manager {
|
||||
public:
|
||||
display_chat_manager(display & disp) : my_disp_(disp) {}
|
||||
|
||||
void add_observer(const std::string& name) { observers_.insert(name); }
|
||||
void remove_observer(const std::string& name) { observers_.erase(name); }
|
||||
const std::set<std::string>& observers() const { return observers_; }
|
||||
|
||||
void add_chat_message(const time_t& time, const std::string& speaker,
|
||||
int side, const std::string& msg, events::chat_handler::MESSAGE_TYPE type, bool bell);
|
||||
void clear_chat_messages() { prune_chat_messages(true); }
|
||||
|
||||
friend class game_display; //needed because it calls prune_chat_message
|
||||
private:
|
||||
std::set<std::string> observers_;
|
||||
|
||||
struct chat_message
|
||||
{
|
||||
chat_message(int speaker, int h);
|
||||
|
||||
int speaker_handle;
|
||||
int handle;
|
||||
boost::uint32_t created_at;
|
||||
};
|
||||
|
||||
void prune_chat_messages(bool remove_all=false);
|
||||
|
||||
std::vector<chat_message> chat_messages_;
|
||||
|
||||
display & my_disp_;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -14,8 +14,9 @@
|
|||
*/
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
#include "floating_textbox.hpp"
|
||||
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "game_display.hpp"
|
||||
#include "game_preferences.hpp"
|
||||
#include "log.hpp"
|
||||
|
@ -136,7 +137,7 @@ namespace gui{
|
|||
text.append(line_start ? ": " : " ");
|
||||
} else if (matches.size() > 1) {
|
||||
std::string completion_list = utils::join(matches, " ");
|
||||
resources::screen->add_chat_message(time(NULL), "", 0, completion_list,
|
||||
resources::screen->get_chat_manager().add_chat_message(time(NULL), "", 0, completion_list,
|
||||
events::chat_handler::MESSAGE_PRIVATE, false);
|
||||
}
|
||||
box_->set_text(text);
|
||||
|
|
|
@ -23,17 +23,8 @@
|
|||
#include "gettext.hpp"
|
||||
#include "wesconfig.h"
|
||||
|
||||
#ifdef HAVE_LIBDBUS
|
||||
#include <dbus/dbus.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GROWL
|
||||
#include <Growl/GrowlApplicationBridge-Carbon.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
Growl_Delegate growl_obj;
|
||||
#endif
|
||||
|
||||
#include "cursor.hpp"
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "fake_unit_manager.hpp"
|
||||
#include "fake_unit_ptr.hpp"
|
||||
#include "game_board.hpp"
|
||||
|
@ -43,6 +34,7 @@ Growl_Delegate growl_obj;
|
|||
#include "map.hpp"
|
||||
#include "map_label.hpp"
|
||||
#include "marked-up_text.hpp"
|
||||
#include "notifications.hpp"
|
||||
#include "reports.hpp"
|
||||
#include "resources.hpp"
|
||||
#include "tod_manager.hpp"
|
||||
|
@ -50,9 +42,6 @@ Growl_Delegate growl_obj;
|
|||
#include "unit.hpp"
|
||||
#include "unit_drawer.hpp"
|
||||
#include "whiteboard/manager.hpp"
|
||||
#ifdef _WIN32
|
||||
#include "windows_tray_notification.hpp"
|
||||
#endif
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
|
@ -86,8 +75,7 @@ game_display::game_display(game_board& board, CVideo& video, boost::weak_ptr<wb:
|
|||
sidebarScaling_(1.0),
|
||||
first_turn_(true),
|
||||
in_game_(false),
|
||||
observers_(),
|
||||
chat_messages_(),
|
||||
chat_man_(new display_chat_manager(*this)),
|
||||
game_mode_(RUNNING)
|
||||
{
|
||||
replace_overlay_map(&overlay_map_);
|
||||
|
@ -108,7 +96,7 @@ game_display::~game_display()
|
|||
{
|
||||
try {
|
||||
// SDL_FreeSurface(minimap_);
|
||||
prune_chat_messages(true);
|
||||
chat_man_->prune_chat_messages(true);
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
|
@ -240,7 +228,7 @@ void game_display::pre_draw() {
|
|||
* @todo FIXME: must modify changed, but best to do it at the
|
||||
* floating_label level
|
||||
*/
|
||||
prune_chat_messages();
|
||||
chat_man_->prune_chat_messages();
|
||||
}
|
||||
|
||||
|
||||
|
@ -669,230 +657,6 @@ std::string game_display::current_team_name() const
|
|||
return std::string();
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBDBUS
|
||||
/** Use KDE 4 notifications. */
|
||||
static bool kde_style = false;
|
||||
|
||||
struct wnotify
|
||||
{
|
||||
wnotify()
|
||||
: id()
|
||||
, owner()
|
||||
, message()
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t id;
|
||||
std::string owner;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
static std::list<wnotify> notifications;
|
||||
|
||||
static DBusHandlerResult filter_dbus_signal(DBusConnection *, DBusMessage *buf, void *)
|
||||
{
|
||||
if (!dbus_message_is_signal(buf, "org.freedesktop.Notifications", "NotificationClosed")) {
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
uint32_t id;
|
||||
dbus_message_get_args(buf, NULL,
|
||||
DBUS_TYPE_UINT32, &id,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
std::list<wnotify>::iterator i = notifications.begin(),
|
||||
i_end = notifications.end();
|
||||
while (i != i_end && i->id != id) ++i;
|
||||
if (i != i_end)
|
||||
notifications.erase(i);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
static DBusConnection *get_dbus_connection()
|
||||
{
|
||||
if (preferences::get("disable_notifications", false)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool initted = false;
|
||||
static DBusConnection *connection = NULL;
|
||||
|
||||
if (!initted)
|
||||
{
|
||||
initted = true;
|
||||
if (getenv("KDE_SESSION_VERSION")) {
|
||||
// This variable is defined for KDE 4 only.
|
||||
kde_style = true;
|
||||
}
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
|
||||
if (!connection) {
|
||||
ERR_DP << "Failed to open DBus session: " << err.message << '\n';
|
||||
dbus_error_free(&err);
|
||||
return NULL;
|
||||
}
|
||||
dbus_connection_add_filter(connection, filter_dbus_signal, NULL, NULL);
|
||||
}
|
||||
if (connection) {
|
||||
dbus_connection_read_write(connection, 0);
|
||||
while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) {}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
static uint32_t send_dbus_notification(DBusConnection *connection, uint32_t replaces_id,
|
||||
const std::string &owner, const std::string &message)
|
||||
{
|
||||
DBusMessage *buf = dbus_message_new_method_call(
|
||||
kde_style ? "org.kde.VisualNotifications" : "org.freedesktop.Notifications",
|
||||
kde_style ? "/VisualNotifications" : "/org/freedesktop/Notifications",
|
||||
kde_style ? "org.kde.VisualNotifications" : "org.freedesktop.Notifications",
|
||||
"Notify");
|
||||
const char *app_name = "Battle for Wesnoth";
|
||||
dbus_message_append_args(buf,
|
||||
DBUS_TYPE_STRING, &app_name,
|
||||
DBUS_TYPE_UINT32, &replaces_id,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (kde_style) {
|
||||
const char *event_id = "";
|
||||
dbus_message_append_args(buf,
|
||||
DBUS_TYPE_STRING, &event_id,
|
||||
DBUS_TYPE_INVALID);
|
||||
}
|
||||
std::string app_icon_ = game_config::path + "/images/wesnoth-icon-small.png";
|
||||
const char *app_icon = app_icon_.c_str();
|
||||
const char *summary = owner.c_str();
|
||||
const char *body = message.c_str();
|
||||
const char **actions = NULL;
|
||||
dbus_message_append_args(buf,
|
||||
DBUS_TYPE_STRING, &app_icon,
|
||||
DBUS_TYPE_STRING, &summary,
|
||||
DBUS_TYPE_STRING, &body,
|
||||
DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &actions, 0,
|
||||
DBUS_TYPE_INVALID);
|
||||
DBusMessageIter iter, hints;
|
||||
dbus_message_iter_init_append(buf, &iter);
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &hints);
|
||||
dbus_message_iter_close_container(&iter, &hints);
|
||||
int expire_timeout = kde_style ? 5000 : -1;
|
||||
dbus_message_append_args(buf,
|
||||
DBUS_TYPE_INT32, &expire_timeout,
|
||||
DBUS_TYPE_INVALID);
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
DBusMessage *ret = dbus_connection_send_with_reply_and_block(connection, buf, 1000, &err);
|
||||
dbus_message_unref(buf);
|
||||
if (!ret) {
|
||||
ERR_DP << "Failed to send visual notification: " << err.message << '\n';
|
||||
dbus_error_free(&err);
|
||||
if (kde_style) {
|
||||
ERR_DP << " Retrying with the freedesktop protocol." << std::endl;
|
||||
kde_style = false;
|
||||
return send_dbus_notification(connection, replaces_id, owner, message);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
uint32_t id;
|
||||
dbus_message_get_args(ret, NULL,
|
||||
DBUS_TYPE_UINT32, &id,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_message_unref(ret);
|
||||
// TODO: remove once closing signals for KDE are handled in filter_dbus_signal.
|
||||
if (kde_style) return 0;
|
||||
return id;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LIBDBUS) || defined(HAVE_GROWL) || defined(_WIN32)
|
||||
void game_display::send_notification(const std::string& owner, const std::string& message)
|
||||
{
|
||||
if (preferences::get("disable_notifications", false)) { return; }
|
||||
#else
|
||||
void game_display::send_notification(const std::string& /*owner*/, const std::string& /*message*/)
|
||||
{
|
||||
#endif
|
||||
#if defined(HAVE_LIBDBUS) || defined(HAVE_GROWL) || defined(_WIN32)
|
||||
Uint8 app_state = SDL_GetAppState();
|
||||
|
||||
// Do not show notifications when the window is visible...
|
||||
if ((app_state & SDL_APPACTIVE) != 0)
|
||||
{
|
||||
// ... and it has a focus.
|
||||
if ((app_state & (SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS)) != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBDBUS
|
||||
DBusConnection *connection = get_dbus_connection();
|
||||
if (!connection) return;
|
||||
|
||||
std::list<wnotify>::iterator i = notifications.begin(),
|
||||
i_end = notifications.end();
|
||||
while (i != i_end && i->owner != owner) ++i;
|
||||
|
||||
if (i != i_end) {
|
||||
i->message = message + "\n" + i->message;
|
||||
int endl_pos = -1;
|
||||
for (int ctr = 0; ctr < 5; ctr++)
|
||||
endl_pos = i->message.find('\n', endl_pos+1);
|
||||
|
||||
i->message = i->message.substr(0,endl_pos);
|
||||
|
||||
send_dbus_notification(connection, i->id, owner, i->message);
|
||||
return;
|
||||
} else {
|
||||
uint32_t id = send_dbus_notification(connection, 0, owner, message);
|
||||
if (!id) return;
|
||||
wnotify visual;
|
||||
visual.id = id;
|
||||
visual.owner = owner;
|
||||
visual.message = message;
|
||||
notifications.push_back(visual);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GROWL
|
||||
CFStringRef app_name = CFStringCreateWithCString(NULL, "Wesnoth", kCFStringEncodingUTF8);
|
||||
CFStringRef cf_owner = CFStringCreateWithCString(NULL, owner.c_str(), kCFStringEncodingUTF8);
|
||||
CFStringRef cf_message = CFStringCreateWithCString(NULL, message.c_str(), kCFStringEncodingUTF8);
|
||||
//Should be changed as soon as there are more than 2 types of notifications
|
||||
CFStringRef cf_note_name = CFStringCreateWithCString(NULL, owner == _("Turn changed") ? _("Turn changed") : _("Chat message"), kCFStringEncodingUTF8);
|
||||
|
||||
growl_obj.applicationName = app_name;
|
||||
growl_obj.registrationDictionary = NULL;
|
||||
growl_obj.applicationIconData = NULL;
|
||||
growl_obj.growlIsReady = NULL;
|
||||
growl_obj.growlNotificationWasClicked = NULL;
|
||||
growl_obj.growlNotificationTimedOut = NULL;
|
||||
|
||||
Growl_SetDelegate(&growl_obj);
|
||||
Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext(cf_owner, cf_message, cf_note_name, NULL, NULL, NULL, NULL);
|
||||
|
||||
CFRelease(app_name);
|
||||
CFRelease(cf_owner);
|
||||
CFRelease(cf_message);
|
||||
CFRelease(cf_note_name);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string notification_title;
|
||||
std::string notification_message;
|
||||
|
||||
if (owner == _("Turn changed")) {
|
||||
notification_title = owner;
|
||||
notification_message = message;
|
||||
} else {
|
||||
notification_title = _("Chat message");
|
||||
notification_message = owner + ": " + message;
|
||||
}
|
||||
|
||||
windows_tray_notification::show(notification_title, notification_message);
|
||||
#endif
|
||||
}
|
||||
|
||||
void game_display::set_playing_team(size_t teamindex)
|
||||
{
|
||||
|
@ -908,160 +672,4 @@ void game_display::begin_game()
|
|||
invalidate_all();
|
||||
}
|
||||
|
||||
namespace {
|
||||
const int chat_message_border = 5;
|
||||
const int chat_message_x = 10;
|
||||
const SDL_Color chat_message_color = {255,255,255,255};
|
||||
const SDL_Color chat_message_bg = {0,0,0,140};
|
||||
}
|
||||
|
||||
void game_display::add_chat_message(const time_t& time, const std::string& speaker,
|
||||
int side, const std::string& message, events::chat_handler::MESSAGE_TYPE type,
|
||||
bool bell)
|
||||
{
|
||||
const bool whisper = speaker.find("whisper: ") == 0;
|
||||
std::string sender = speaker;
|
||||
if (whisper) {
|
||||
sender.assign(speaker, 9, speaker.size());
|
||||
}
|
||||
if (!preferences::parse_should_show_lobby_join(sender, message)) return;
|
||||
if (preferences::is_ignored(sender)) return;
|
||||
|
||||
preferences::parse_admin_authentication(sender, message);
|
||||
|
||||
if (bell) {
|
||||
if ((type == events::chat_handler::MESSAGE_PRIVATE && (!resources::gameboard->is_observer() || whisper))
|
||||
|| utils::word_match(message, preferences::login())) {
|
||||
sound::play_UI_sound(game_config::sounds::receive_message_highlight);
|
||||
} else if (preferences::is_friend(sender)) {
|
||||
sound::play_UI_sound(game_config::sounds::receive_message_friend);
|
||||
} else if (sender == "server") {
|
||||
sound::play_UI_sound(game_config::sounds::receive_message_server);
|
||||
} else {
|
||||
sound::play_UI_sound(game_config::sounds::receive_message);
|
||||
}
|
||||
}
|
||||
|
||||
bool action = false;
|
||||
|
||||
std::string msg;
|
||||
|
||||
if (message.find("/me ") == 0) {
|
||||
msg.assign(message, 4, message.size());
|
||||
action = true;
|
||||
} else {
|
||||
msg += message;
|
||||
}
|
||||
|
||||
try {
|
||||
// We've had a joker who send an invalid utf-8 message to crash clients
|
||||
// so now catch the exception and ignore the message.
|
||||
msg = font::word_wrap_text(msg,font::SIZE_SMALL,map_outside_area().w*3/4);
|
||||
} catch (utf8::invalid_utf8_exception&) {
|
||||
ERR_NG << "Invalid utf-8 found, chat message is ignored." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
int ypos = chat_message_x;
|
||||
for(std::vector<chat_message>::const_iterator m = chat_messages_.begin(); m != chat_messages_.end(); ++m) {
|
||||
ypos += std::max(font::get_floating_label_rect(m->handle).h,
|
||||
font::get_floating_label_rect(m->speaker_handle).h);
|
||||
}
|
||||
SDL_Color speaker_color = {255,255,255,255};
|
||||
if(side >= 1) {
|
||||
speaker_color = int_to_color(team::get_side_color_range(side).mid());
|
||||
}
|
||||
|
||||
SDL_Color message_color = chat_message_color;
|
||||
std::stringstream str;
|
||||
std::stringstream message_str;
|
||||
|
||||
if(type == events::chat_handler::MESSAGE_PUBLIC) {
|
||||
if(action) {
|
||||
str << "<" << speaker << " " << msg << ">";
|
||||
message_color = speaker_color;
|
||||
message_str << " ";
|
||||
} else {
|
||||
if (!speaker.empty())
|
||||
str << "<" << speaker << ">";
|
||||
message_str << msg;
|
||||
}
|
||||
} else {
|
||||
if(action) {
|
||||
str << "*" << speaker << " " << msg << "*";
|
||||
message_color = speaker_color;
|
||||
message_str << " ";
|
||||
} else {
|
||||
if (!speaker.empty())
|
||||
str << "*" << speaker << "*";
|
||||
message_str << msg;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepend message with timestamp.
|
||||
std::stringstream message_complete;
|
||||
message_complete << preferences::get_chat_timestamp(time) << str.str();
|
||||
|
||||
const SDL_Rect rect = map_outside_area();
|
||||
|
||||
font::floating_label spk_flabel(message_complete.str());
|
||||
spk_flabel.set_font_size(font::SIZE_SMALL);
|
||||
spk_flabel.set_color(speaker_color);
|
||||
spk_flabel.set_position(rect.x + chat_message_x, rect.y + ypos);
|
||||
spk_flabel.set_clip_rect(rect);
|
||||
spk_flabel.set_alignment(font::LEFT_ALIGN);
|
||||
spk_flabel.set_bg_color(chat_message_bg);
|
||||
spk_flabel.set_border_size(chat_message_border);
|
||||
spk_flabel.use_markup(false);
|
||||
|
||||
int speaker_handle = font::add_floating_label(spk_flabel);
|
||||
|
||||
font::floating_label msg_flabel(message_str.str());
|
||||
msg_flabel.set_font_size(font::SIZE_SMALL);
|
||||
msg_flabel.set_color(message_color);
|
||||
msg_flabel.set_position(rect.x + chat_message_x + font::get_floating_label_rect(speaker_handle).w,
|
||||
rect.y + ypos);
|
||||
msg_flabel.set_clip_rect(rect);
|
||||
msg_flabel.set_alignment(font::LEFT_ALIGN);
|
||||
msg_flabel.set_bg_color(chat_message_bg);
|
||||
msg_flabel.set_border_size(chat_message_border);
|
||||
msg_flabel.use_markup(false);
|
||||
|
||||
int message_handle = font::add_floating_label(msg_flabel);
|
||||
|
||||
// Send system notification if appropriate.
|
||||
send_notification(speaker, message);
|
||||
|
||||
chat_messages_.push_back(chat_message(speaker_handle,message_handle));
|
||||
|
||||
prune_chat_messages();
|
||||
}
|
||||
|
||||
void game_display::prune_chat_messages(bool remove_all)
|
||||
{
|
||||
const unsigned message_aging = preferences::chat_message_aging();
|
||||
const unsigned message_ttl = remove_all ? 0 : message_aging * 60 * 1000;
|
||||
const unsigned max_chat_messages = preferences::chat_lines();
|
||||
int movement = 0;
|
||||
|
||||
if(message_aging != 0 || remove_all || chat_messages_.size() > max_chat_messages) {
|
||||
while (!chat_messages_.empty() &&
|
||||
(chat_messages_.front().created_at + message_ttl < SDL_GetTicks() ||
|
||||
chat_messages_.size() > max_chat_messages))
|
||||
{
|
||||
const chat_message &old = chat_messages_.front();
|
||||
movement += font::get_floating_label_rect(old.handle).h;
|
||||
font::remove_floating_label(old.speaker_handle);
|
||||
font::remove_floating_label(old.handle);
|
||||
chat_messages_.erase(chat_messages_.begin());
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const chat_message &cm, chat_messages_) {
|
||||
font::move_floating_label(cm.speaker_handle, 0, - movement);
|
||||
font::move_floating_label(cm.handle, 0, - movement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#define GAME_DISPLAY_H_INCLUDED
|
||||
|
||||
class config;
|
||||
class display_chat_manager;
|
||||
class tod_manager;
|
||||
class team;
|
||||
class unit_map;
|
||||
|
@ -166,8 +167,6 @@ public:
|
|||
//void draw_terrain_palette(int x, int y, terrain_type::TERRAIN selected);
|
||||
t_translation::t_terrain get_terrain_on(int palx, int paly, int x, int y);
|
||||
|
||||
void send_notification(const std::string& owner, const std::string& message);
|
||||
|
||||
/**
|
||||
* Sets the team controlled by the player using the computer.
|
||||
*
|
||||
|
@ -193,13 +192,7 @@ public:
|
|||
|
||||
std::string current_team_name() const;
|
||||
|
||||
void add_observer(const std::string& name) { observers_.insert(name); }
|
||||
void remove_observer(const std::string& name) { observers_.erase(name); }
|
||||
const std::set<std::string>& observers() const { return observers_; }
|
||||
|
||||
void add_chat_message(const time_t& time, const std::string& speaker,
|
||||
int side, const std::string& msg, events::chat_handler::MESSAGE_TYPE type, bool bell);
|
||||
void clear_chat_messages() { prune_chat_messages(true); }
|
||||
display_chat_manager & get_chat_manager() { return *chat_man_; }
|
||||
|
||||
void begin_game();
|
||||
|
||||
|
@ -248,23 +241,7 @@ private:
|
|||
|
||||
bool first_turn_, in_game_;
|
||||
|
||||
std::set<std::string> observers_;
|
||||
|
||||
struct chat_message
|
||||
{
|
||||
chat_message(int speaker, int h) : speaker_handle(speaker), handle(h), created_at(SDL_GetTicks())
|
||||
{}
|
||||
|
||||
int speaker_handle;
|
||||
int handle;
|
||||
Uint32 created_at;
|
||||
};
|
||||
|
||||
void prune_chat_messages(bool remove_all=false);
|
||||
|
||||
std::vector<chat_message> chat_messages_;
|
||||
|
||||
|
||||
boost::scoped_ptr<display_chat_manager> chat_man_;
|
||||
|
||||
tgame_mode game_mode_;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "conditional_wml.hpp"
|
||||
#include "handlers.hpp"
|
||||
|
||||
#include "../display_chat_manager.hpp"
|
||||
#include "../game_config.hpp"
|
||||
#include "../game_display.hpp"
|
||||
#include "../game_data.hpp"
|
||||
|
@ -336,7 +337,7 @@ namespace { // Support functions
|
|||
msg << " (" << itor->second << ")";
|
||||
}
|
||||
|
||||
resources::screen->add_chat_message(time(NULL), caption, 0, msg.str(),
|
||||
resources::screen->get_chat_manager().add_chat_message(time(NULL), caption, 0, msg.str(),
|
||||
events::chat_handler::MESSAGE_PUBLIC, false);
|
||||
if ( to_cerr )
|
||||
std::cerr << caption << ": " << msg.str() << '\n';
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "actions/vision.hpp"
|
||||
#include "ai/manager.hpp"
|
||||
#include "dialogs.hpp"
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "filechooser.hpp"
|
||||
#include "formatter.hpp"
|
||||
#include "formula_string_utils.hpp"
|
||||
|
@ -1398,7 +1399,7 @@ void menu_handler::add_chat_message(const time_t& time,
|
|||
const std::string& speaker, int side, const std::string& message,
|
||||
events::chat_handler::MESSAGE_TYPE type)
|
||||
{
|
||||
gui_->add_chat_message(time, speaker, side, message, type, false);
|
||||
gui_->get_chat_manager().add_chat_message(time, speaker, side, message, type, false);
|
||||
}
|
||||
|
||||
//simple command args parser, separated from command_handler for clarity.
|
||||
|
@ -2801,7 +2802,7 @@ void console_handler::do_controller()
|
|||
}
|
||||
|
||||
void console_handler::do_clear() {
|
||||
menu_handler_.gui_->clear_chat_messages();
|
||||
menu_handler_.gui_->get_chat_manager().clear_chat_messages();
|
||||
}
|
||||
void console_handler::do_sunset() {
|
||||
int delay = lexical_cast_default<int>(get_data());
|
||||
|
@ -3341,7 +3342,7 @@ void menu_handler::ai_formula()
|
|||
|
||||
void menu_handler::clear_messages()
|
||||
{
|
||||
gui_->clear_chat_messages(); // also clear debug-messages and WML-error-messages
|
||||
gui_->get_chat_manager().clear_chat_messages(); // also clear debug-messages and WML-error-messages
|
||||
}
|
||||
|
||||
void menu_handler::change_controller(const std::string& side, const std::string& controller)
|
||||
|
|
|
@ -20,11 +20,13 @@
|
|||
|
||||
#include "ai/configuration.hpp"
|
||||
#include "dialogs.hpp"
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "game_display.hpp"
|
||||
#include "game_preferences.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "log.hpp"
|
||||
#include "map.hpp"
|
||||
#include "notifications.hpp"
|
||||
#include "wml_separators.hpp"
|
||||
#include "sound.hpp"
|
||||
|
||||
|
@ -559,7 +561,7 @@ void connect::process_network_data(const config& data,
|
|||
if (!was_able_to_start && engine_.can_start_game()) {
|
||||
DBG_MP << "play party full sound" << std::endl;
|
||||
sound::play_UI_sound(game_config::sounds::party_full_bell);
|
||||
game_display::get_singleton()->send_notification(_("Wesnoth"), _ ("Ready to start!"));
|
||||
notifications::send_notification(_("Wesnoth"), _ ("Ready to start!"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "marked-up_text.hpp"
|
||||
#include "mp_game_utils.hpp"
|
||||
#include "multiplayer_wait.hpp"
|
||||
#include "notifications.hpp"
|
||||
#include "resources.hpp"
|
||||
#include "statistics.hpp"
|
||||
#include "saved_game.hpp"
|
||||
|
@ -432,7 +433,7 @@ void wait::start_game()
|
|||
|
||||
LOG_NW << "starting game\n";
|
||||
sound::play_UI_sound(game_config::sounds::mp_game_begins);
|
||||
game_display::get_singleton()->send_notification(_("Wesnoth"), _ ("Game has begun!"));
|
||||
notifications::send_notification(_("Wesnoth"), _ ("Game has begun!"));
|
||||
}
|
||||
|
||||
void wait::layout_children(const SDL_Rect& rect)
|
||||
|
|
264
src/notifications.cpp
Normal file
264
src/notifications.cpp
Normal file
|
@ -0,0 +1,264 @@
|
|||
#include "notifications.hpp"
|
||||
#include "global.hpp"
|
||||
|
||||
#include "log.hpp"
|
||||
#include "game_preferences.hpp"
|
||||
|
||||
#include "game_config.hpp"
|
||||
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <cstdlib>
|
||||
#include <list>
|
||||
|
||||
#include "SDL_active.h"
|
||||
|
||||
#ifdef HAVE_LIBDBUS
|
||||
#include <dbus/dbus.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GROWL
|
||||
#include <Growl/GrowlApplicationBridge-Carbon.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
Growl_Delegate growl_obj;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows_tray_notification.hpp"
|
||||
#endif
|
||||
|
||||
static lg::log_domain log_display("display");
|
||||
#define ERR_DP LOG_STREAM(err, log_display)
|
||||
#define LOG_DP LOG_STREAM(info, log_display)
|
||||
|
||||
using boost::uint32_t;
|
||||
|
||||
namespace notifications
|
||||
{
|
||||
|
||||
#ifdef HAVE_LIBDBUS
|
||||
/** Use KDE 4 notifications. */
|
||||
static bool kde_style = false;
|
||||
|
||||
struct wnotify
|
||||
{
|
||||
wnotify()
|
||||
: id()
|
||||
, owner()
|
||||
, message()
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t id;
|
||||
std::string owner;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
static std::list<wnotify> notifications;
|
||||
|
||||
static DBusHandlerResult filter_dbus_signal(DBusConnection *, DBusMessage *buf, void *)
|
||||
{
|
||||
if (!dbus_message_is_signal(buf, "org.freedesktop.Notifications", "NotificationClosed")) {
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
uint32_t id;
|
||||
dbus_message_get_args(buf, NULL,
|
||||
DBUS_TYPE_UINT32, &id,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
std::list<wnotify>::iterator i = notifications.begin(),
|
||||
i_end = notifications.end();
|
||||
while (i != i_end && i->id != id) ++i;
|
||||
if (i != i_end)
|
||||
notifications.erase(i);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
static DBusConnection *get_dbus_connection()
|
||||
{
|
||||
if (preferences::get("disable_notifications", false)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool initted = false;
|
||||
static DBusConnection *connection = NULL;
|
||||
|
||||
if (!initted)
|
||||
{
|
||||
initted = true;
|
||||
if (getenv("KDE_SESSION_VERSION")) {
|
||||
// This variable is defined for KDE 4 only.
|
||||
kde_style = true;
|
||||
}
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
|
||||
if (!connection) {
|
||||
ERR_DP << "Failed to open DBus session: " << err.message << '\n';
|
||||
dbus_error_free(&err);
|
||||
return NULL;
|
||||
}
|
||||
dbus_connection_add_filter(connection, filter_dbus_signal, NULL, NULL);
|
||||
}
|
||||
if (connection) {
|
||||
dbus_connection_read_write(connection, 0);
|
||||
while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) {}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
static uint32_t send_dbus_notification(DBusConnection *connection, uint32_t replaces_id,
|
||||
const std::string &owner, const std::string &message)
|
||||
{
|
||||
DBusMessage *buf = dbus_message_new_method_call(
|
||||
kde_style ? "org.kde.VisualNotifications" : "org.freedesktop.Notifications",
|
||||
kde_style ? "/VisualNotifications" : "/org/freedesktop/Notifications",
|
||||
kde_style ? "org.kde.VisualNotifications" : "org.freedesktop.Notifications",
|
||||
"Notify");
|
||||
const char *app_name = "Battle for Wesnoth";
|
||||
dbus_message_append_args(buf,
|
||||
DBUS_TYPE_STRING, &app_name,
|
||||
DBUS_TYPE_UINT32, &replaces_id,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (kde_style) {
|
||||
const char *event_id = "";
|
||||
dbus_message_append_args(buf,
|
||||
DBUS_TYPE_STRING, &event_id,
|
||||
DBUS_TYPE_INVALID);
|
||||
}
|
||||
std::string app_icon_ = game_config::path + "/images/wesnoth-icon-small.png";
|
||||
const char *app_icon = app_icon_.c_str();
|
||||
const char *summary = owner.c_str();
|
||||
const char *body = message.c_str();
|
||||
const char **actions = NULL;
|
||||
dbus_message_append_args(buf,
|
||||
DBUS_TYPE_STRING, &app_icon,
|
||||
DBUS_TYPE_STRING, &summary,
|
||||
DBUS_TYPE_STRING, &body,
|
||||
DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &actions, 0,
|
||||
DBUS_TYPE_INVALID);
|
||||
DBusMessageIter iter, hints;
|
||||
dbus_message_iter_init_append(buf, &iter);
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &hints);
|
||||
dbus_message_iter_close_container(&iter, &hints);
|
||||
int expire_timeout = kde_style ? 5000 : -1;
|
||||
dbus_message_append_args(buf,
|
||||
DBUS_TYPE_INT32, &expire_timeout,
|
||||
DBUS_TYPE_INVALID);
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
DBusMessage *ret = dbus_connection_send_with_reply_and_block(connection, buf, 1000, &err);
|
||||
dbus_message_unref(buf);
|
||||
if (!ret) {
|
||||
ERR_DP << "Failed to send visual notification: " << err.message << '\n';
|
||||
dbus_error_free(&err);
|
||||
if (kde_style) {
|
||||
ERR_DP << " Retrying with the freedesktop protocol." << std::endl;
|
||||
kde_style = false;
|
||||
return send_dbus_notification(connection, replaces_id, owner, message);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
uint32_t id;
|
||||
dbus_message_get_args(ret, NULL,
|
||||
DBUS_TYPE_UINT32, &id,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_message_unref(ret);
|
||||
// TODO: remove once closing signals for KDE are handled in filter_dbus_signal.
|
||||
if (kde_style) return 0;
|
||||
return id;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LIBDBUS) || defined(HAVE_GROWL) || defined(_WIN32)
|
||||
void send_notification(const std::string& owner, const std::string& message)
|
||||
{
|
||||
if (preferences::get("disable_notifications", false)) { return; }
|
||||
#else
|
||||
void send_notification(const std::string& /*owner*/, const std::string& /*message*/)
|
||||
{
|
||||
#endif
|
||||
#if defined(HAVE_LIBDBUS) || defined(HAVE_GROWL) || defined(_WIN32)
|
||||
Uint8 app_state = SDL_GetAppState();
|
||||
|
||||
// Do not show notifications when the window is visible...
|
||||
if ((app_state & SDL_APPACTIVE) != 0)
|
||||
{
|
||||
// ... and it has a focus.
|
||||
if ((app_state & (SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS)) != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBDBUS
|
||||
DBusConnection *connection = get_dbus_connection();
|
||||
if (!connection) return;
|
||||
|
||||
std::list<wnotify>::iterator i = notifications.begin(),
|
||||
i_end = notifications.end();
|
||||
while (i != i_end && i->owner != owner) ++i;
|
||||
|
||||
if (i != i_end) {
|
||||
i->message = message + "\n" + i->message;
|
||||
int endl_pos = -1;
|
||||
for (int ctr = 0; ctr < 5; ctr++)
|
||||
endl_pos = i->message.find('\n', endl_pos+1);
|
||||
|
||||
i->message = i->message.substr(0,endl_pos);
|
||||
|
||||
send_dbus_notification(connection, i->id, owner, i->message);
|
||||
return;
|
||||
} else {
|
||||
uint32_t id = send_dbus_notification(connection, 0, owner, message);
|
||||
if (!id) return;
|
||||
wnotify visual;
|
||||
visual.id = id;
|
||||
visual.owner = owner;
|
||||
visual.message = message;
|
||||
notifications.push_back(visual);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GROWL
|
||||
CFStringRef app_name = CFStringCreateWithCString(NULL, "Wesnoth", kCFStringEncodingUTF8);
|
||||
CFStringRef cf_owner = CFStringCreateWithCString(NULL, owner.c_str(), kCFStringEncodingUTF8);
|
||||
CFStringRef cf_message = CFStringCreateWithCString(NULL, message.c_str(), kCFStringEncodingUTF8);
|
||||
//Should be changed as soon as there are more than 2 types of notifications
|
||||
CFStringRef cf_note_name = CFStringCreateWithCString(NULL, owner == _("Turn changed") ? _("Turn changed") : _("Chat message"), kCFStringEncodingUTF8);
|
||||
|
||||
growl_obj.applicationName = app_name;
|
||||
growl_obj.registrationDictionary = NULL;
|
||||
growl_obj.applicationIconData = NULL;
|
||||
growl_obj.growlIsReady = NULL;
|
||||
growl_obj.growlNotificationWasClicked = NULL;
|
||||
growl_obj.growlNotificationTimedOut = NULL;
|
||||
|
||||
Growl_SetDelegate(&growl_obj);
|
||||
Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext(cf_owner, cf_message, cf_note_name, NULL, NULL, NULL, NULL);
|
||||
|
||||
CFRelease(app_name);
|
||||
CFRelease(cf_owner);
|
||||
CFRelease(cf_message);
|
||||
CFRelease(cf_note_name);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string notification_title;
|
||||
std::string notification_message;
|
||||
|
||||
if (owner == _("Turn changed")) {
|
||||
notification_title = owner;
|
||||
notification_message = message;
|
||||
} else {
|
||||
notification_title = _("Chat message");
|
||||
notification_message = owner + ": " + message;
|
||||
}
|
||||
|
||||
windows_tray_notification::show(notification_title, notification_message);
|
||||
#endif
|
||||
}
|
||||
|
||||
} //end namespace notifications
|
6
src/notifications.hpp
Normal file
6
src/notifications.hpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
#include <string>
|
||||
|
||||
namespace notifications
|
||||
{
|
||||
void send_notification(const std::string& owner, const std::string& message);
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
#include "ai/manager.hpp"
|
||||
#include "ai/testing.hpp"
|
||||
#include "dialogs.hpp"
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "formula_string_utils.hpp"
|
||||
#include "game_events/handlers.hpp"
|
||||
#include "game_events/menu_item.hpp"
|
||||
|
@ -462,7 +463,7 @@ void play_controller::fire_start(bool execute){
|
|||
if( saved_game_.classification().random_mode != "" && (network::nconnections() != 0))
|
||||
{
|
||||
std::string mes = _("MP game uses an alternative random mode, if you don't know what this message means, then most likeley someone is cheating or someone reloaded a corrupt game.");
|
||||
gui_->add_chat_message(
|
||||
gui_->get_chat_manager().add_chat_message(
|
||||
time(NULL),
|
||||
"game_engine",
|
||||
0,
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
#include "dialogs.hpp"
|
||||
|
||||
#include "actions/undo.hpp"
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "game_end_exceptions.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "log.hpp"
|
||||
#include "notifications.hpp"
|
||||
#include "playturn.hpp"
|
||||
#include "preferences.hpp"
|
||||
#include "resources.hpp"
|
||||
|
@ -102,7 +104,7 @@ possible_end_play_signal playmp_controller::play_side()
|
|||
player["name"] = current_team().current_player();
|
||||
std::string turn_notification_msg = _("$name has taken control");
|
||||
turn_notification_msg = utils::interpolate_variables_into_string(turn_notification_msg, &player);
|
||||
gui_->send_notification(_("Turn changed"), turn_notification_msg);
|
||||
notifications::send_notification(_("Turn changed"), turn_notification_msg);
|
||||
|
||||
// Proceed with the parent function.
|
||||
return playsingle_controller::play_side();
|
||||
|
@ -586,7 +588,7 @@ bool playmp_controller::can_execute_command(const hotkey::hotkey_command& cmd, i
|
|||
|
||||
void playmp_controller::do_idle_notification()
|
||||
{
|
||||
resources::screen->add_chat_message(time(NULL), "", 0,
|
||||
resources::screen->get_chat_manager().add_chat_message(time(NULL), "", 0,
|
||||
_("This side is in an idle state. To proceed with the game, it must be assigned to another controller. You may use :droid, :control or :give_control for example."),
|
||||
events::chat_handler::MESSAGE_PUBLIC, false);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "ai/game_info.hpp"
|
||||
#include "ai/testing.hpp"
|
||||
#include "dialogs.hpp"
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "game_end_exceptions.hpp"
|
||||
#include "game_events/pump.hpp"
|
||||
#include "game_preferences.hpp"
|
||||
|
@ -990,7 +991,7 @@ void playsingle_controller::play_ai_turn(){
|
|||
*/
|
||||
void playsingle_controller::do_idle_notification()
|
||||
{
|
||||
resources::screen->add_chat_message(time(NULL), "Wesnoth", 0,
|
||||
resources::screen->get_chat_manager().add_chat_message(time(NULL), "Wesnoth", 0,
|
||||
"This side is in an idle state. To proceed with the game, the host must assign it to another controller.",
|
||||
events::chat_handler::MESSAGE_PUBLIC, false);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "actions/undo.hpp" // for undo_list
|
||||
#include "chat_events.hpp" // for chat_handler, etc
|
||||
#include "config.hpp" // for config, etc
|
||||
#include "display_chat_manager.hpp" // for add_chat_message, add_observer, etc
|
||||
#include "formula_string_utils.hpp" // for vgettext
|
||||
#include "game_board.hpp" // for game_board
|
||||
#include "game_display.hpp" // for game_display
|
||||
|
@ -143,23 +144,23 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
|
|||
|
||||
if (const config &msg = cfg.child("message"))
|
||||
{
|
||||
resources::screen->add_chat_message(time(NULL), msg["sender"], msg["side"],
|
||||
resources::screen->get_chat_manager().add_chat_message(time(NULL), msg["sender"], msg["side"],
|
||||
msg["message"], events::chat_handler::MESSAGE_PUBLIC,
|
||||
preferences::message_bell());
|
||||
}
|
||||
else if (const config &msg = cfg.child("whisper") /*&& is_observer()*/)
|
||||
{
|
||||
resources::screen->add_chat_message(time(NULL), "whisper: " + msg["sender"].str(), 0,
|
||||
resources::screen->get_chat_manager().add_chat_message(time(NULL), "whisper: " + msg["sender"].str(), 0,
|
||||
msg["message"], events::chat_handler::MESSAGE_PRIVATE,
|
||||
preferences::message_bell());
|
||||
}
|
||||
else if (const config &ob = cfg.child("observer") )
|
||||
{
|
||||
resources::screen->add_observer(ob["name"]);
|
||||
resources::screen->get_chat_manager().add_observer(ob["name"]);
|
||||
}
|
||||
else if (const config &ob = cfg.child("observer_quit"))
|
||||
{
|
||||
resources::screen->remove_observer(ob["name"]);
|
||||
resources::screen->get_chat_manager().remove_observer(ob["name"]);
|
||||
}
|
||||
else if (cfg.child("leave_game")) {
|
||||
throw network::error("");
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "actions/undo.hpp"
|
||||
#include "config_assign.hpp"
|
||||
#include "dialogs.hpp"
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "game_display.hpp"
|
||||
#include "game_preferences.hpp"
|
||||
#include "game_data.hpp"
|
||||
|
@ -681,14 +682,14 @@ static void check_checksums(const config &cfg)
|
|||
if (!u.valid()) {
|
||||
std::stringstream message;
|
||||
message << "non existent unit to checksum at " << loc.x+1 << "," << loc.y+1 << "!";
|
||||
resources::screen->add_chat_message(time(NULL), "verification", 1, message.str(),
|
||||
resources::screen->get_chat_manager().add_chat_message(time(NULL), "verification", 1, message.str(),
|
||||
events::chat_handler::MESSAGE_PRIVATE, false);
|
||||
continue;
|
||||
}
|
||||
if (get_checksum(*u) != ch["value"]) {
|
||||
std::stringstream message;
|
||||
message << "checksum mismatch at " << loc.x+1 << "," << loc.y+1 << "!";
|
||||
resources::screen->add_chat_message(time(NULL), "verification", 1, message.str(),
|
||||
resources::screen->get_chat_manager().add_chat_message(time(NULL), "verification", 1, message.str(),
|
||||
events::chat_handler::MESSAGE_PRIVATE, false);
|
||||
}
|
||||
}
|
||||
|
@ -768,7 +769,7 @@ REPLAY_RETURN do_replay_handle()
|
|||
get_replay_source().add_chat_message_location();
|
||||
if (!get_replay_source().is_skipping() || is_whisper) {
|
||||
int side = child["side"];
|
||||
resources::screen->add_chat_message(get_time(child), speaker_name, side, message,
|
||||
resources::screen->get_chat_manager().add_chat_message(get_time(child), speaker_name, side, message,
|
||||
(team_name.empty() ? events::chat_handler::MESSAGE_PUBLIC
|
||||
: events::chat_handler::MESSAGE_PRIVATE),
|
||||
preferences::message_bell());
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "carryover.hpp"
|
||||
#include "actions/vision.hpp"
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "game_end_exceptions.hpp"
|
||||
#include "game_errors.hpp" //needed to be thrown
|
||||
#include "game_events/handlers.hpp"
|
||||
|
@ -294,7 +295,7 @@ void replay_controller::reset_replay()
|
|||
{
|
||||
DBG_REPLAY << "replay_controller::reset_replay\n";
|
||||
|
||||
gui_->clear_chat_messages();
|
||||
gui_->get_chat_manager().clear_chat_messages();
|
||||
is_playing_ = false;
|
||||
player_number_ = 1;
|
||||
current_turn_ = 1;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "ai/manager.hpp" // for manager, holder
|
||||
#include "attack_prediction.hpp" // for combatant
|
||||
#include "config.hpp" // for config, etc
|
||||
#include "display_chat_manager.hpp" // for clear_chat_messages
|
||||
#include "filesystem.hpp" // for get_wml_location
|
||||
#include "font.hpp" // for LABEL_COLOR
|
||||
#include "game_board.hpp" // for game_board
|
||||
|
@ -1722,7 +1723,7 @@ static int intf_debug(lua_State* L)
|
|||
*/
|
||||
static int intf_clear_messages(lua_State*)
|
||||
{
|
||||
resources::screen->clear_chat_messages();
|
||||
resources::screen->get_chat_manager().clear_chat_messages();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "lua/lauxlib.h"
|
||||
|
||||
#include "config.hpp"
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "game_display.hpp"
|
||||
#include "log.hpp"
|
||||
#include "recall_list_manager.hpp"
|
||||
|
@ -40,7 +41,7 @@ static lg::log_domain log_scripting_lua("scripting/lua");
|
|||
|
||||
void chat_message(std::string const &caption, std::string const &msg)
|
||||
{
|
||||
resources::screen->add_chat_message(time(NULL), caption, 0, msg,
|
||||
resources::screen->get_chat_manager().add_chat_message(time(NULL), caption, 0, msg,
|
||||
events::chat_handler::MESSAGE_PUBLIC, false);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue