Bug Summary

File:daemon/daemon.c
Warning:line 1405, column 40
Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name daemon.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/rootdir/src/daemon -fcoverage-compilation-dir=/rootdir/src/daemon -resource-dir /usr/lib/llvm-19/lib/clang/19 -D HAVE_CONFIG_H -I . -I ../.. -I ../.. -I /usr/include/ctk-3.0 -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/gio-unix-2.0 -I /usr/include/atk-1.0 -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -D _REENTRANT -D _REENTRANT -I /usr/local/include/libvnck-3.0 -I /usr/include/startup-notification-1.0 -D ENGINES_DIR="/usr/lib/cafe-notification-daemon/engines" -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2025-01-09-182947-38283-1 -x c daemon.c
1/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * Copyright (C) 2006 Christian Hammond <chipx86@chipx86.com>
4 * Copyright (C) 2005 John (J5) Palmieri <johnp@redhat.com>
5 * Copyright (C) 2010 Red Hat, Inc.
6 * Copyright (C) 2011 Perberos <perberos@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301, USA.
22 */
23
24#include "config.h"
25
26#include <stdlib.h>
27#include <errno(*__errno_location ()).h>
28#include <string.h>
29#include <stdio.h>
30
31#include <glib/gi18n.h>
32#include <glib.h>
33#include <glib-object.h>
34#include <ctk/ctk.h>
35
36#include <X11/Xproto.h>
37
38#include <X11/Xlib.h>
39#include <X11/Xutil.h>
40#include <X11/Xatom.h>
41#include <cdk/cdkx.h>
42
43#define VNCK_I_KNOW_THIS_IS_UNSTABLE
44#include <libvnck/libvnck.h>
45
46#include "daemon.h"
47#include "engines.h"
48#include "stack.h"
49#include "sound.h"
50#include "mnd-dbus-generated.h"
51
52#define MAX_NOTIFICATIONS20 20
53#define IMAGE_SIZE48 48
54#define IDLE_SECONDS30 30
55#define NOTIFICATION_BUS_NAME"org.freedesktop.Notifications" "org.freedesktop.Notifications"
56#define NOTIFICATION_BUS_PATH"/org/freedesktop/Notifications" "/org/freedesktop/Notifications"
57
58#define NW_GET_NOTIFY_ID(nw)(((guint) (gulong) (g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_id"))))
\
59 (GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(nw), "_notify_id"))((guint) (gulong) (g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_id")))
)
60#define NW_GET_NOTIFY_SENDER(nw)(g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_sender"))
\
61 (g_object_get_data(G_OBJECT(nw)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((nw)), (((GType) ((20) << (2))))))))
, "_notify_sender"))
62#define NW_GET_DAEMON(nw)(g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_daemon"))
\
63 (g_object_get_data(G_OBJECT(nw)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((nw)), (((GType) ((20) << (2))))))))
, "_notify_daemon"))
64
65enum {
66 PROP_0,
67 PROP_REPLACE,
68 LAST_PROP
69};
70
71typedef struct {
72 NotifyStackLocation type;
73 const char* identifier;
74} PopupNotifyStackLocation;
75
76const PopupNotifyStackLocation popup_stack_locations[] = {
77 {NOTIFY_STACK_LOCATION_TOP_LEFT, "top_left"},
78 {NOTIFY_STACK_LOCATION_TOP_RIGHT, "top_right"},
79 {NOTIFY_STACK_LOCATION_BOTTOM_LEFT, "bottom_left"},
80 {NOTIFY_STACK_LOCATION_BOTTOM_RIGHT, "bottom_right"},
81 {NOTIFY_STACK_LOCATION_UNKNOWN, NULL((void*)0)}
82};
83
84#define POPUP_STACK_DEFAULT_INDEX3 3 /* XXX Hack! */
85
86typedef struct {
87 NotifyDaemon* daemon;
88 GTimeVal expiration;
89 GTimeVal paused_diff;
90 guint id;
91 CtkWindow* nw;
92 Window src_window_xid;
93 guint has_timeout : 1;
94 guint paused : 1;
95} NotifyTimeout;
96
97typedef struct {
98 NotifyStack** stacks;
99 int n_stacks;
100 Atom workarea_atom;
101} NotifyScreen;
102
103struct _NotifyDaemon {
104 GObject parent;
105 GSettings* gsettings;
106 guint next_id;
107 guint timeout_source;
108 guint exit_timeout_source;
109 GHashTable* idle_reposition_notify_ids;
110 GHashTable* monitored_window_hash;
111 GHashTable* notification_hash;
112 gboolean url_clicked_lock;
113
114 NotifyDaemonNotifications *skeleton;
115 guint bus_name_id;
116 gboolean replace;
117
118 NotifyStackLocation stack_location;
119 NotifyScreen* screen;
120};
121
122typedef struct {
123 guint id;
124 NotifyDaemon* daemon;
125} _NotifyPendingClose;
126
127static void notify_daemon_finalize(GObject* object);
128static void _notification_destroyed_cb(CtkWindow* nw, NotifyDaemon* daemon);
129static void _close_notification(NotifyDaemon* daemon, guint id, gboolean hide_notification, NotifydClosedReason reason);
130static CdkFilterReturn _notify_x11_filter(CdkXEvent* xevent, CdkEvent* event, NotifyDaemon* daemon);
131static void _emit_closed_signal(CtkWindow* nw, NotifydClosedReason reason);
132static void _action_invoked_cb(CtkWindow* nw, const char* key);
133static NotifyStackLocation get_stack_location_from_string(const gchar *slocation);
134static void sync_notification_position(NotifyDaemon* daemon, CtkWindow* nw, Window source);
135static void monitor_notification_source_windows(NotifyDaemon* daemon, NotifyTimeout* nt, Window source);
136static GParamSpec *properties[LAST_PROP] = { NULL((void*)0) };
137
138static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object, GDBusMethodInvocation *invocation, const gchar *app_name, guint id, const gchar *icon, const gchar *summary, const gchar *body, const gchar *const *actions, GVariant *hints, gint timeout, gpointer user_data);
139static gboolean notify_daemon_close_notification_handler(NotifyDaemonNotifications *object, GDBusMethodInvocation *invocation, guint arg_id, gpointer user_data);
140static gboolean notify_daemon_get_capabilities( NotifyDaemonNotifications *object, GDBusMethodInvocation *invocation);
141static gboolean notify_daemon_get_server_information (NotifyDaemonNotifications *object, GDBusMethodInvocation *invocation, gpointer user_data);
142
143G_DEFINE_TYPE(NotifyDaemon, notify_daemon, G_TYPE_OBJECT)static void notify_daemon_init (NotifyDaemon *self); static void
notify_daemon_class_init (NotifyDaemonClass *klass); static GType
notify_daemon_get_type_once (void); static gpointer notify_daemon_parent_class
= ((void*)0); static gint NotifyDaemon_private_offset; static
void notify_daemon_class_intern_init (gpointer klass) { notify_daemon_parent_class
= g_type_class_peek_parent (klass); if (NotifyDaemon_private_offset
!= 0) g_type_class_adjust_private_offset (klass, &NotifyDaemon_private_offset
); notify_daemon_class_init ((NotifyDaemonClass*) klass); } __attribute__
((__unused__)) static inline gpointer notify_daemon_get_instance_private
(NotifyDaemon *self) { return (((gpointer) ((guint8*) (self)
+ (glong) (NotifyDaemon_private_offset)))); } GType notify_daemon_get_type
(void) { static GType static_g_define_type_id = 0; if ((__extension__
({ _Static_assert (sizeof *(&static_g_define_type_id) ==
sizeof (gpointer), "Expression evaluates to false"); (void) (
0 ? (gpointer) * (&static_g_define_type_id) : ((void*)0))
; (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id
) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= notify_daemon_get_type_once (); (__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id
) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer
((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id
)); })) ; } return static_g_define_type_id; } __attribute__ (
(__noinline__)) static GType notify_daemon_get_type_once (void
) { GType g_define_type_id = g_type_register_static_simple ((
(GType) ((20) << (2))), g_intern_static_string ("NotifyDaemon"
), sizeof (NotifyDaemonClass), (GClassInitFunc)(void (*)(void
)) notify_daemon_class_intern_init, sizeof (NotifyDaemon), (GInstanceInitFunc
)(void (*)(void)) notify_daemon_init, (GTypeFlags) 0); { {{};
} } return g_define_type_id; }
;
144
145static void bus_acquired_handler_cb (GDBusConnection *connection,
146 const gchar *name,
147 gpointer user_data)
148{
149 NotifyDaemon *daemon;
150
151 GError *error = NULL((void*)0);
152 gboolean exported;
153
154 daemon = NOTIFY_DAEMON (user_data);
155
156 g_signal_connect (daemon->skeleton, "handle-notify", G_CALLBACK (notify_daemon_notify_handler), daemon)g_signal_connect_data ((daemon->skeleton), ("handle-notify"
), (((GCallback) (notify_daemon_notify_handler))), (daemon), (
(void*)0), (GConnectFlags) 0)
;
157 g_signal_connect (daemon->skeleton, "handle-close-notification", G_CALLBACK (notify_daemon_close_notification_handler), daemon)g_signal_connect_data ((daemon->skeleton), ("handle-close-notification"
), (((GCallback) (notify_daemon_close_notification_handler)))
, (daemon), ((void*)0), (GConnectFlags) 0)
;
158 g_signal_connect (daemon->skeleton, "handle-get-capabilities", G_CALLBACK (notify_daemon_get_capabilities), daemon)g_signal_connect_data ((daemon->skeleton), ("handle-get-capabilities"
), (((GCallback) (notify_daemon_get_capabilities))), (daemon)
, ((void*)0), (GConnectFlags) 0)
;
159 g_signal_connect (daemon->skeleton, "handle-get-server-information", G_CALLBACK (notify_daemon_get_server_information), daemon)g_signal_connect_data ((daemon->skeleton), ("handle-get-server-information"
), (((GCallback) (notify_daemon_get_server_information))), (daemon
), ((void*)0), (GConnectFlags) 0)
;
160
161 exported = g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (daemon->skeleton)((((GDBusInterfaceSkeleton*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((daemon->skeleton)), ((g_dbus_interface_skeleton_get_type
()))))))
,
162 connection, NOTIFICATION_BUS_PATH"/org/freedesktop/Notifications", &error);
163 if (!exported)
164 {
165 g_warning ("Failed to export interface: %s", error->message);
166 g_error_free (error);
167
168 ctk_main_quit();
169 }
170}
171
172static void name_lost_handler_cb (GDBusConnection *connection,
173 const gchar *name,
174 gpointer user_data)
175{
176 g_debug("bus name lost\n");
177 ctk_main_quit();
178}
179
180
181static void notify_daemon_constructed (GObject *object)
182{
183 NotifyDaemon *daemon;
184
185 GBusNameOwnerFlags flags;
186
187 daemon = NOTIFY_DAEMON (object);
188
189 G_OBJECT_CLASS (notify_daemon_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((notify_daemon_parent_class)), (((GType) ((20) << (
2))))))))
->constructed (object);
190
191 flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
192 if (daemon->replace)
193 flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
194
195 daemon->bus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION,
196 NOTIFICATION_BUS_NAME"org.freedesktop.Notifications", flags,
197 bus_acquired_handler_cb, NULL((void*)0),
198 name_lost_handler_cb, daemon, NULL((void*)0));
199}
200
201static void notify_daemon_set_property (GObject *object,
202 guint prop_id,
203 const GValue *value,
204 GParamSpec *pspec)
205{
206 NotifyDaemon *daemon;
207
208 daemon = NOTIFY_DAEMON (object);
209
210 switch (prop_id)
211 {
212 case PROP_REPLACE:
213 daemon->replace = g_value_get_boolean (value);
214 break;
215 default:
216 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)do { GObject *_glib__object = (GObject*) ((object)); GParamSpec
*_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id
= ((prop_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'"
, "daemon.c", 216, ("property"), _glib__property_id, _glib__pspec
->name, g_type_name ((((((GTypeClass*) (((GTypeInstance*) (
_glib__pspec))->g_class))->g_type)))), (g_type_name (((
(((GTypeClass*) (((GTypeInstance*) (_glib__object))->g_class
))->g_type)))))); } while (0)
;
217 break;
218 }
219}
220
221static void notify_daemon_class_init(NotifyDaemonClass* daemon_class)
222{
223 GObjectClass* object_class = G_OBJECT_CLASS(daemon_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((daemon_class)), (((GType) ((20) << (2))))))))
;
224 object_class->set_property = notify_daemon_set_property;
225
226 object_class->constructed = notify_daemon_constructed;
227 object_class->finalize = notify_daemon_finalize;
228 properties[PROP_REPLACE] =
229 g_param_spec_boolean ("replace", "replace", "replace", FALSE(0),
230 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
231 G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
)
);
232
233 g_object_class_install_properties (object_class, LAST_PROP, properties);
234}
235
236static void _notify_timeout_destroy(NotifyTimeout* nt)
237{
238 /*
239 * Disconnect the destroy handler to avoid a loop since the id
240 * won't be removed from the hash table before the widget is
241 * destroyed.
242 */
243 g_signal_handlers_disconnect_by_func(nt->nw, _notification_destroyed_cb, nt->daemon)g_signal_handlers_disconnect_matched ((nt->nw), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), (_notification_destroyed_cb), (nt->daemon))
;
244 ctk_widget_destroy(CTK_WIDGET(nt->nw)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((nt->nw)), ((ctk_widget_get_type ()))))))
);
245 g_free(nt);
246}
247
248static gboolean do_exit(gpointer user_data)
249{
250 exit(0);
251 return FALSE(0);
252}
253
254static void add_exit_timeout(NotifyDaemon* daemon)
255{
256 g_assert (daemon != NULL)do { if (daemon != ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "daemon.c", 256, ((const char*) (__func__)), "daemon != NULL"
); } while (0)
;
257
258 if (daemon->exit_timeout_source > 0)
259 return;
260
261 daemon->exit_timeout_source = g_timeout_add_seconds(IDLE_SECONDS30, do_exit, NULL((void*)0));
262}
263
264static void remove_exit_timeout(NotifyDaemon* daemon)
265{
266 g_assert (daemon != NULL)do { if (daemon != ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "daemon.c", 266, ((const char*) (__func__)), "daemon != NULL"
); } while (0)
;
267
268 if (daemon->exit_timeout_source == 0)
269 return;
270
271 g_source_remove(daemon->exit_timeout_source);
272 daemon->exit_timeout_source = 0;
273}
274
275static int
276_ctk_get_monitor_num (CdkMonitor *monitor)
277{
278 CdkDisplay *display;
279 int n_monitors, i;
280
281 display = cdk_monitor_get_display(monitor);
282 n_monitors = cdk_display_get_n_monitors(display);
283
284 for(i = 0; i < n_monitors; i++)
285 {
286 if (cdk_display_get_monitor(display, i) == monitor) return i;
287 }
288
289 return -1;
290}
291
292static void create_stack_for_monitor(NotifyDaemon* daemon, CdkScreen* screen, CdkMonitor *monitor_num)
293{
294 NotifyScreen* nscreen = daemon->screen;
295
296 nscreen->stacks[_ctk_get_monitor_num(monitor_num)] = notify_stack_new(daemon, screen, monitor_num, daemon->stack_location);
297}
298
299static void on_screen_monitors_changed(CdkScreen* screen, NotifyDaemon* daemon)
300{
301 CdkDisplay *display;
302 NotifyScreen* nscreen;
303 int n_monitors;
304 int i;
305
306 nscreen = daemon->screen;
307 display = cdk_screen_get_display (screen);
308
309 n_monitors = cdk_display_get_n_monitors(display);
310
311 if (n_monitors > nscreen->n_stacks)
312 {
313 /* grow */
314 nscreen->stacks = g_renew(NotifyStack *, nscreen->stacks, n_monitors)((NotifyStack * *) g_realloc_n (nscreen->stacks, (n_monitors
), sizeof (NotifyStack *)))
;
315
316 /* add more stacks */
317 for (i = nscreen->n_stacks; i < n_monitors; i++)
318 {
319 create_stack_for_monitor(daemon, screen, cdk_display_get_monitor (display, i));
320 }
321
322 nscreen->n_stacks = n_monitors;
323 }
324 else if (n_monitors < nscreen->n_stacks)
325 {
326 NotifyStack* last_stack;
327
328 last_stack = nscreen->stacks[n_monitors - 1];
329
330 /* transfer items before removing stacks */
331 for (i = n_monitors; i < nscreen->n_stacks; i++)
332 {
333 NotifyStack* stack = nscreen->stacks[i];
334 GList* windows = g_list_copy(notify_stack_get_windows(stack));
335 GList* l;
336
337 for (l = windows; l != NULL((void*)0); l = l->next)
338 {
339 /* skip removing the window from the old stack since it will try
340 * to unrealize the window.
341 * And the stack is going away anyhow. */
342 notify_stack_add_window(last_stack, l->data, TRUE(!(0)));
343 }
344
345 g_list_free(windows);
346 notify_stack_destroy(stack);
347 nscreen->stacks[i] = NULL((void*)0);
348 }
349
350 /* remove the extra stacks */
351 nscreen->stacks = g_renew(NotifyStack*, nscreen->stacks, n_monitors)((NotifyStack* *) g_realloc_n (nscreen->stacks, (n_monitors
), sizeof (NotifyStack*)))
;
352 nscreen->n_stacks = n_monitors;
353 }
354}
355
356static void create_stacks_for_screen(NotifyDaemon* daemon, CdkScreen *screen)
357{
358 CdkDisplay *display;
359 NotifyScreen* nscreen;
360 int i;
361
362 nscreen = daemon->screen;
363 display = cdk_screen_get_display (screen);
364
365 nscreen->n_stacks = cdk_display_get_n_monitors(display);
366
367 nscreen->stacks = g_renew(NotifyStack*, nscreen->stacks, nscreen->n_stacks)((NotifyStack* *) g_realloc_n (nscreen->stacks, (nscreen->
n_stacks), sizeof (NotifyStack*)))
;
368
369 for (i = 0; i < nscreen->n_stacks; i++)
370 {
371 create_stack_for_monitor(daemon, screen, cdk_display_get_monitor (display, i));
372 }
373}
374
375static CdkFilterReturn screen_xevent_filter(CdkXEvent* xevent, CdkEvent* event, NotifyScreen* nscreen)
376{
377 XEvent* xev = (XEvent*) xevent;
378
379 if (xev->type == PropertyNotify28 && xev->xproperty.atom == nscreen->workarea_atom)
380 {
381 int i;
382
383 for (i = 0; i < nscreen->n_stacks; i++)
384 {
385 notify_stack_queue_update_position(nscreen->stacks[i]);
386 }
387 }
388
389 return CDK_FILTER_CONTINUE;
390}
391
392static void create_screen(NotifyDaemon* daemon)
393{
394 CdkDisplay *display;
395 CdkScreen *screen;
396 CdkWindow *cdkwindow;
397
398 g_assert(daemon->screen == NULL)do { if (daemon->screen == ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "daemon.c", 398, ((const char*) (__func__)), "daemon->screen == NULL"
); } while (0)
;
399
400 display = cdk_display_get_default();
401 screen = cdk_display_get_default_screen (display);
402
403 g_signal_connect(screen, "monitors-changed", G_CALLBACK(on_screen_monitors_changed), daemon)g_signal_connect_data ((screen), ("monitors-changed"), (((GCallback
) (on_screen_monitors_changed))), (daemon), ((void*)0), (GConnectFlags
) 0)
;
404
405 daemon->screen = g_new0(NotifyScreen, 1)((NotifyScreen *) g_malloc0_n ((1), sizeof (NotifyScreen)));
406
407 daemon->screen->workarea_atom = XInternAtom(CDK_DISPLAY_XDISPLAY (display)(cdk_x11_display_get_xdisplay (display)), "_NET_WORKAREA", True1);
408
409 cdkwindow = cdk_screen_get_root_window(screen);
410 cdk_window_add_filter(cdkwindow, (CdkFilterFunc) screen_xevent_filter, daemon->screen);
411 cdk_window_set_events(cdkwindow, cdk_window_get_events(cdkwindow) | CDK_PROPERTY_CHANGE_MASK);
412
413 create_stacks_for_screen(daemon, screen);
414}
415
416static void on_popup_location_changed(GSettings *settings, gchar *key, NotifyDaemon* daemon)
417{
418 NotifyStackLocation stack_location;
419 gchar *slocation;
420 int i;
421
422 slocation = g_settings_get_string(daemon->gsettings, key);
423
424 if (slocation != NULL((void*)0) && *slocation != '\0')
425 {
426 stack_location = get_stack_location_from_string(slocation);
427 }
428 else
429 {
430 g_settings_set_string (daemon->gsettings, GSETTINGS_KEY_POPUP_LOCATION"popup-location", popup_stack_locations[POPUP_STACK_DEFAULT_INDEX3].identifier);
431
432 stack_location = NOTIFY_STACK_LOCATION_DEFAULT;
433 }
434
435 daemon->stack_location = stack_location;
436 g_free(slocation);
437
438 NotifyScreen *nscreen;
439
440 nscreen = daemon->screen;
441 for (i = 0; i < nscreen->n_stacks; i++)
442 {
443 NotifyStack* stack;
444 stack = nscreen->stacks[i];
445 notify_stack_set_location(stack, stack_location);
446 }
447}
448
449static void notify_daemon_init(NotifyDaemon* daemon)
450{
451 gchar *location;
452
453 daemon->next_id = 1;
454 daemon->timeout_source = 0;
455 daemon->skeleton = notify_daemon_notifications_skeleton_new ();
456
457 add_exit_timeout(daemon);
458
459 daemon->gsettings = g_settings_new (GSETTINGS_SCHEMA"org.cafe.NotificationDaemon");
460
461 g_signal_connect (daemon->gsettings, "changed::" GSETTINGS_KEY_POPUP_LOCATION, G_CALLBACK (on_popup_location_changed), daemon)g_signal_connect_data ((daemon->gsettings), ("changed::" "popup-location"
), (((GCallback) (on_popup_location_changed))), (daemon), ((void
*)0), (GConnectFlags) 0)
;
462
463 location = g_settings_get_string (daemon->gsettings, GSETTINGS_KEY_POPUP_LOCATION"popup-location");
464 daemon->stack_location = get_stack_location_from_string(location);
465 g_free(location);
466
467 daemon->screen = NULL((void*)0);
468
469 create_screen(daemon);
470
471 daemon->idle_reposition_notify_ids = g_hash_table_new(NULL((void*)0), NULL((void*)0));
472 daemon->monitored_window_hash = g_hash_table_new(NULL((void*)0), NULL((void*)0));
473 daemon->notification_hash = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, (GDestroyNotify) _notify_timeout_destroy);
474}
475
476static void destroy_screen(NotifyDaemon* daemon)
477{
478 CdkDisplay *display;
479 CdkScreen *screen;
480 CdkWindow *cdkwindow;
481 gint i;
482
483 display = cdk_display_get_default();
484 screen = cdk_display_get_default_screen (display);
485
486 g_signal_handlers_disconnect_by_func (screen,g_signal_handlers_disconnect_matched ((screen), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), (((GCallback) (on_screen_monitors_changed))), (daemon))
487 G_CALLBACK (on_screen_monitors_changed),g_signal_handlers_disconnect_matched ((screen), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), (((GCallback) (on_screen_monitors_changed))), (daemon))
488 daemon)g_signal_handlers_disconnect_matched ((screen), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), (((GCallback) (on_screen_monitors_changed))), (daemon))
;
489
490 cdkwindow = cdk_screen_get_root_window (screen);
491 cdk_window_remove_filter (cdkwindow, (CdkFilterFunc) screen_xevent_filter, daemon->screen);
492 for (i = 0; i < daemon->screen->n_stacks; i++) {
493 g_clear_object (&daemon->screen->stacks[i])do { _Static_assert (sizeof *((&daemon->screen->stacks
[i])) == sizeof (gpointer), "Expression evaluates to false");
__typeof__ (((&daemon->screen->stacks[i]))) _pp = (
(&daemon->screen->stacks[i])); __typeof__ (*((&
daemon->screen->stacks[i]))) _ptr = *_pp; *_pp = ((void
*)0); if (_ptr) (g_object_unref) (_ptr); } while (0)
;
494 }
495
496 g_free (daemon->screen->stacks);
497 daemon->screen->stacks = NULL((void*)0);
498
499 g_free(daemon->screen);
500 daemon->screen = NULL((void*)0);
501}
502
503static void notify_daemon_finalize(GObject* object)
504{
505 NotifyDaemon* daemon;
506
507 daemon = NOTIFY_DAEMON(object);
508
509 if (g_hash_table_size(daemon->monitored_window_hash) > 0)
510 {
511 cdk_window_remove_filter(NULL((void*)0), (CdkFilterFunc) _notify_x11_filter, daemon);
512 }
513
514 if (daemon->skeleton != NULL((void*)0))
515 {
516 GDBusInterfaceSkeleton *skeleton;
517
518 skeleton = G_DBUS_INTERFACE_SKELETON (daemon->skeleton)((((GDBusInterfaceSkeleton*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((daemon->skeleton)), ((g_dbus_interface_skeleton_get_type
()))))))
;
519 g_dbus_interface_skeleton_unexport (skeleton);
520
521 g_clear_object (&daemon->skeleton)do { _Static_assert (sizeof *((&daemon->skeleton)) == sizeof
(gpointer), "Expression evaluates to false"); __typeof__ (((
&daemon->skeleton))) _pp = ((&daemon->skeleton)
); __typeof__ (*((&daemon->skeleton))) _ptr = *_pp; *_pp
= ((void*)0); if (_ptr) (g_object_unref) (_ptr); } while (0)
;
522 }
523
524 remove_exit_timeout(daemon);
525
526 g_hash_table_destroy(daemon->monitored_window_hash);
527 g_hash_table_destroy(daemon->idle_reposition_notify_ids);
528 g_hash_table_destroy(daemon->notification_hash);
529
530 destroy_screen(daemon);
531
532 if (daemon->bus_name_id > 0)
533 {
534 g_bus_unown_name (daemon->bus_name_id);
535 daemon->bus_name_id = 0;
536 }
537
538 G_OBJECT_CLASS(notify_daemon_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((notify_daemon_parent_class)), (((GType) ((20) << (
2))))))))
->finalize(object);
539}
540
541static NotifyStackLocation get_stack_location_from_string(const gchar *slocation)
542{
543 NotifyStackLocation stack_location = NOTIFY_STACK_LOCATION_DEFAULT;
544
545 if (slocation == NULL((void*)0) || *slocation == '\0')
546 {
547 return NOTIFY_STACK_LOCATION_DEFAULT;
548 }
549 else
550 {
551 const PopupNotifyStackLocation* l;
552
553 for (l = popup_stack_locations; l->type != NOTIFY_STACK_LOCATION_UNKNOWN; l++)
554 {
555 if (!strcmp(slocation, l->identifier))
556 {
557 stack_location = l->type;
558 }
559 }
560 }
561
562 return stack_location;
563}
564
565static void _action_invoked_cb(CtkWindow* nw, const char *key)
566{
567 NotifyDaemon* daemon;
568 guint id;
569
570 daemon = NW_GET_DAEMON(nw)(g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_daemon"))
;
571 id = NW_GET_NOTIFY_ID(nw)(((guint) (gulong) (g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_id"))))
;
572
573 notify_daemon_notifications_emit_action_invoked (daemon->skeleton, id, key);
574
575 _close_notification(daemon, id, TRUE(!(0)), NOTIFYD_CLOSED_USER);
576}
577
578static void _emit_closed_signal(CtkWindow* nw, NotifydClosedReason reason)
579{
580 guint id;
581 NotifyDaemon* daemon;
582
583 id = NW_GET_NOTIFY_ID(nw)(((guint) (gulong) (g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_id"))))
;
584 daemon = NW_GET_DAEMON(nw)(g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_daemon"))
;
585
586 notify_daemon_notifications_emit_notification_closed(daemon->skeleton, id, reason);
587}
588
589static void _close_notification(NotifyDaemon* daemon, guint id, gboolean hide_notification, NotifydClosedReason reason)
590{
591 NotifyTimeout* nt;
592
593 nt = (NotifyTimeout*) g_hash_table_lookup(daemon->notification_hash, &id);
594
595 if (nt != NULL((void*)0))
596 {
597 _emit_closed_signal(nt->nw, reason);
598
599 if (hide_notification)
600 {
601 theme_hide_notification(nt->nw);
602 }
603
604 g_hash_table_remove(daemon->notification_hash, &id);
605
606 if (g_hash_table_size(daemon->notification_hash) == 0)
607 {
608 add_exit_timeout(daemon);
609 }
610 }
611}
612
613static gboolean _close_notification_not_shown(_NotifyPendingClose* data)
614{
615 _close_notification(data->daemon, data->id, TRUE(!(0)), NOTIFYD_CLOSED_RESERVED);
616 g_object_unref(data->daemon);
617 g_free(data);
618
619 return FALSE(0);
620}
621
622static void _notification_destroyed_cb(CtkWindow* nw, NotifyDaemon* daemon)
623{
624 /*
625 * This usually won't happen, but can if notification-daemon dies before
626 * all notifications are closed. Mark them as expired.
627 */
628 _close_notification(daemon, NW_GET_NOTIFY_ID(nw)(((guint) (gulong) (g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_id"))))
, FALSE(0), NOTIFYD_CLOSED_EXPIRED);
629}
630
631typedef struct {
632 NotifyDaemon* daemon;
633 gint id;
634} IdleRepositionData;
635
636static gboolean idle_reposition_notification(IdleRepositionData* data)
637{
638 NotifyDaemon* daemon;
639 NotifyTimeout* nt;
640 gint notify_id;
641
642 daemon = data->daemon;
643 notify_id = data->id;
644
645 /* Look up the timeout, if it's completed we don't need to do anything */
646 nt = (NotifyTimeout*) g_hash_table_lookup(daemon->notification_hash, &notify_id);
647
648 if (nt != NULL((void*)0))
649 {
650 sync_notification_position(daemon, nt->nw, nt->src_window_xid);
651 }
652
653 g_hash_table_remove(daemon->idle_reposition_notify_ids, GINT_TO_POINTER(notify_id)((gpointer) (glong) (notify_id)));
654 g_object_unref(daemon);
655 g_free(data);
656
657 return FALSE(0);
658}
659
660static void _queue_idle_reposition_notification(NotifyDaemon* daemon, gint notify_id)
661{
662 IdleRepositionData* data;
663 gpointer orig_key;
664 gpointer value;
665 guint idle_id;
666
667 /* Do we already have an idle update pending? */
668 if (g_hash_table_lookup_extended(daemon->idle_reposition_notify_ids, GINT_TO_POINTER(notify_id)((gpointer) (glong) (notify_id)), &orig_key, &value))
669 {
670 return;
671 }
672
673 data = g_new0(IdleRepositionData, 1)((IdleRepositionData *) g_malloc0_n ((1), sizeof (IdleRepositionData
)))
;
674 data->daemon = g_object_ref(daemon)((__typeof__ (daemon)) (g_object_ref) (daemon));
675 data->id = notify_id;
676
677 /* We do this as a short timeout to avoid repositioning spam */
678 idle_id = g_timeout_add_full(G_PRIORITY_LOW300, 50, (GSourceFunc) idle_reposition_notification, data, NULL((void*)0));
679 g_hash_table_insert(daemon->idle_reposition_notify_ids, GINT_TO_POINTER(notify_id)((gpointer) (glong) (notify_id)), GUINT_TO_POINTER(idle_id)((gpointer) (gulong) (idle_id)));
680}
681
682static CdkFilterReturn _notify_x11_filter(CdkXEvent* xevent, CdkEvent* event, NotifyDaemon* daemon)
683{
684 XEvent* xev;
685 gpointer orig_key;
686 gpointer value;
687 gint notify_id;
688 NotifyTimeout* nt;
689
690 xev = (XEvent*) xevent;
691
692 if (xev->xany.type == DestroyNotify17)
693 {
694 g_hash_table_remove(daemon->monitored_window_hash, GUINT_TO_POINTER(xev->xany.window)((gpointer) (gulong) (xev->xany.window)));
695
696 if (g_hash_table_size(daemon->monitored_window_hash) == 0)
697 {
698 cdk_window_remove_filter(NULL((void*)0), (CdkFilterFunc) _notify_x11_filter, daemon);
699 }
700
701 return CDK_FILTER_CONTINUE;
702 }
703
704 if (!g_hash_table_lookup_extended(daemon->monitored_window_hash, GUINT_TO_POINTER(xev->xany.window)((gpointer) (gulong) (xev->xany.window)), &orig_key, &value))
705 {
706 return CDK_FILTER_CONTINUE;
707 }
708
709 notify_id = GPOINTER_TO_INT(value)((gint) (glong) (value));
710
711 if (xev->xany.type == ConfigureNotify22 || xev->xany.type == MapNotify19)
712 {
713 _queue_idle_reposition_notification(daemon, notify_id);
714 }
715 else if (xev->xany.type == ReparentNotify21)
716 {
717 nt = (NotifyTimeout *) g_hash_table_lookup(daemon->notification_hash, &notify_id);
718
719 if (nt == NULL((void*)0))
720 {
721 return CDK_FILTER_CONTINUE;
722 }
723
724 /*
725 * If the window got reparented, we need to start monitoring the
726 * new parents.
727 */
728 monitor_notification_source_windows(daemon, nt, nt->src_window_xid);
729 sync_notification_position(daemon, nt->nw, nt->src_window_xid);
730 }
731
732 return CDK_FILTER_CONTINUE;
733}
734
735static void _mouse_entered_cb(CtkWindow* nw, CdkEventCrossing* event, NotifyDaemon* daemon)
736{
737 NotifyTimeout* nt;
738 guint id;
739 GTimeVal now;
740
741 if (event->detail == CDK_NOTIFY_INFERIOR)
742 {
743 return;
744 }
745
746 id = NW_GET_NOTIFY_ID(nw)(((guint) (gulong) (g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_id"))))
;
747 nt = (NotifyTimeout*) g_hash_table_lookup(daemon->notification_hash, &id);
748
749 nt->paused = TRUE(!(0));
750 g_get_current_time(&now);
751
752 nt->paused_diff.tv_usec = nt->expiration.tv_usec - now.tv_usec;
753 nt->paused_diff.tv_sec = nt->expiration.tv_sec - now.tv_sec;
754
755 if (nt->paused_diff.tv_usec < 0)
756 {
757 nt->paused_diff.tv_usec += G_USEC_PER_SEC1000000;
758 nt->paused_diff.tv_sec--;
759 }
760}
761
762static void _mouse_exitted_cb(CtkWindow* nw, CdkEventCrossing* event, NotifyDaemon* daemon)
763{
764 if (event->detail == CDK_NOTIFY_INFERIOR)
765 {
766 return;
767 }
768
769 guint id = NW_GET_NOTIFY_ID(nw)(((guint) (gulong) (g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_id"))))
;
770 NotifyTimeout* nt = (NotifyTimeout*) g_hash_table_lookup(daemon->notification_hash, &id);
771
772 nt->paused = FALSE(0);
773}
774
775static gboolean _is_expired(gpointer key, NotifyTimeout* nt, gboolean* phas_more_timeouts)
776{
777 time_t now_time;
778 time_t expiration_time;
779 GTimeVal now;
780
781 if (!nt->has_timeout)
782 {
783 return FALSE(0);
784 }
785
786 g_get_current_time(&now);
787
788 expiration_time = (nt->expiration.tv_sec * 1000) + (nt->expiration.tv_usec / 1000);
789 now_time = (now.tv_sec * 1000) + (now.tv_usec / 1000);
790
791 if (now_time > expiration_time)
792 {
793 theme_notification_tick(nt->nw, 0);
794 _emit_closed_signal(nt->nw, NOTIFYD_CLOSED_EXPIRED);
795 return TRUE(!(0));
796 }
797 else if (nt->paused)
798 {
799 nt->expiration.tv_usec = nt->paused_diff.tv_usec + now.tv_usec;
800 nt->expiration.tv_sec = nt->paused_diff.tv_sec + now.tv_sec;
801
802 if (nt->expiration.tv_usec >= G_USEC_PER_SEC1000000)
803 {
804 nt->expiration.tv_usec -= G_USEC_PER_SEC1000000;
805 nt->expiration.tv_sec++;
806 }
807 }
808 else
809 {
810 theme_notification_tick(nt->nw, expiration_time - now_time);
811 }
812
813 *phas_more_timeouts = TRUE(!(0));
814
815 return FALSE(0);
816}
817
818static gboolean _check_expiration(NotifyDaemon* daemon)
819{
820 gboolean has_more_timeouts = FALSE(0);
821
822 g_hash_table_foreach_remove(daemon->notification_hash, (GHRFunc) _is_expired, (gpointer) &has_more_timeouts);
823
824 if (!has_more_timeouts)
825 {
826 daemon->timeout_source = 0;
827
828 if (g_hash_table_size (daemon->notification_hash) == 0)
829 {
830 add_exit_timeout(daemon);
831 }
832 }
833
834 return has_more_timeouts;
835}
836
837static void _calculate_timeout(NotifyDaemon* daemon, NotifyTimeout* nt, int timeout)
838{
839 if (timeout == 0)
840 {
841 nt->has_timeout = FALSE(0);
842 }
843 else
844 {
845 nt->has_timeout = TRUE(!(0));
846
847 if (timeout == -1)
848 {
849 timeout = NOTIFY_DAEMON_DEFAULT_TIMEOUT7000;
850 }
851
852 theme_set_notification_timeout(nt->nw, timeout);
853
854 glong usec = timeout * 1000L; /* convert from msec to usec */
855
856 /*
857 * If it's less than 0, wrap around back to MAXLONG.
858 * g_time_val_add() requires a glong, and anything larger than
859 * MAXLONG will be treated as a negative value.
860 */
861 if (usec < 0)
862 {
863 usec = G_MAXLONG9223372036854775807L;
864 }
865
866 g_get_current_time(&nt->expiration);
867 g_time_val_add(&nt->expiration, usec);
868
869 if (daemon->timeout_source == 0)
870 {
871 daemon->timeout_source = g_timeout_add(100, (GSourceFunc) _check_expiration, daemon);
872 }
873 }
874}
875
876static guint _generate_id(NotifyDaemon* daemon)
877{
878 guint id = 0;
879
880 do {
881 id = daemon->next_id;
882
883 if (id != UINT_MAX(2147483647 *2U +1U))
884 {
885 daemon->next_id++;
886 }
887 else
888 {
889 daemon->next_id = 1;
890 }
891
892 if (g_hash_table_lookup (daemon->notification_hash, &id) != NULL((void*)0))
893 {
894 id = 0;
895 }
896
897 } while (id == 0);
898
899 return id;
900}
901
902static NotifyTimeout* _store_notification(NotifyDaemon* daemon, CtkWindow* nw, int timeout)
903{
904 NotifyTimeout* nt;
905 guint id = _generate_id(daemon);
906
907 nt = g_new0(NotifyTimeout, 1)((NotifyTimeout *) g_malloc0_n ((1), sizeof (NotifyTimeout)));
908 nt->id = id;
909 nt->nw = nw;
910 nt->daemon = daemon;
911
912 _calculate_timeout(daemon, nt, timeout);
913
914 g_hash_table_insert(daemon->notification_hash, g_memdup2(&id, sizeof(guint)), nt);
915 remove_exit_timeout(daemon);
916
917 return nt;
918}
919
920static GdkPixbuf * _notify_daemon_pixbuf_from_data_hint (GVariant *icon_data)
921{
922 gboolean has_alpha;
923 int bits_per_sample;
924 int width;
925 int height;
926 int rowstride;
927 int n_channels;
928 GVariant *data_variant;
929 gsize expected_len;
930 guchar *data;
931 GdkPixbuf *pixbuf;
932
933 g_variant_get (icon_data,
934 "(iiibii@ay)",
935 &width,
936 &height,
937 &rowstride,
938 &has_alpha,
939 &bits_per_sample,
940 &n_channels,
941 &data_variant);
942
943 expected_len = (height - 1) * rowstride + width
944 * ((n_channels * bits_per_sample + 7) / 8);
945
946 if (expected_len != g_variant_get_size (data_variant)) {
947 g_warning ("Expected image data to be of length %" G_GSIZE_FORMAT"lu"
948 " but got a " "length of %" G_GSIZE_FORMAT"lu",
949 expected_len,
950 g_variant_get_size (data_variant));
951 return NULL((void*)0);
952 }
953
954 data = (guchar *) g_memdup2 (g_variant_get_data (data_variant),
955 g_variant_get_size (data_variant));
956
957 pixbuf = gdk_pixbuf_new_from_data (data,
958 GDK_COLORSPACE_RGB,
959 has_alpha,
960 bits_per_sample,
961 width,
962 height,
963 rowstride,
964 (GdkPixbufDestroyNotify) g_free,
965 NULL((void*)0));
966
967 return pixbuf;
968}
969
970static GdkPixbuf* _notify_daemon_pixbuf_from_path(const char* path)
971{
972 GdkPixbuf* pixbuf = NULL((void*)0);
973
974 if (!strncmp (path, "file://", 7) || *path == '/')
975 {
976 if (!strncmp (path, "file://", 7))
977 {
978 path += 7;
979
980 /* Unescape URI-encoded, allowed characters */
981 path = g_uri_unescape_string (path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH"!$&'()*+,;=" ":@" "/");
982 }
983
984 /* Load file */
985 pixbuf = gdk_pixbuf_new_from_file (path, NULL((void*)0));
986 }
987 else
988 {
989 /* Load icon theme icon */
990 CtkIconTheme *theme;
991 CtkIconInfo *icon_info;
992
993 theme = ctk_icon_theme_get_default ();
994 icon_info = ctk_icon_theme_lookup_icon (theme, path, IMAGE_SIZE48, CTK_ICON_LOOKUP_USE_BUILTIN);
995
996 if (icon_info != NULL((void*)0))
997 {
998 gint icon_size;
999
1000 icon_size = MIN (IMAGE_SIZE, ctk_icon_info_get_base_size (icon_info))(((48) < (ctk_icon_info_get_base_size (icon_info))) ? (48)
: (ctk_icon_info_get_base_size (icon_info)))
;
1001
1002 if (icon_size == 0)
1003 {
1004 icon_size = IMAGE_SIZE48;
1005 }
1006
1007 pixbuf = ctk_icon_theme_load_icon (theme, path, icon_size, CTK_ICON_LOOKUP_USE_BUILTIN, NULL((void*)0));
1008
1009 g_object_unref (icon_info);
1010 }
1011
1012 if (pixbuf == NULL((void*)0))
1013 {
1014 /* Well... maybe this is a file afterall. */
1015 pixbuf = gdk_pixbuf_new_from_file (path, NULL((void*)0));
1016 }
1017 }
1018
1019 return pixbuf;
1020}
1021
1022static GdkPixbuf* _notify_daemon_scale_pixbuf(GdkPixbuf *pixbuf, gboolean no_stretch_hint)
1023{
1024 int pw;
1025 int ph;
1026 float scale_factor;
1027
1028 pw = gdk_pixbuf_get_width (pixbuf);
1029 ph = gdk_pixbuf_get_height (pixbuf);
1030
1031 /* Determine which dimension requires the smallest scale. */
1032 scale_factor = (float) IMAGE_SIZE48 / (float) MAX(pw, ph)(((pw) > (ph)) ? (pw) : (ph));
1033
1034 /* always scale down, allow to disable scaling up */
1035 if (scale_factor < 1.0 || !no_stretch_hint)
1036 {
1037 int scale_x;
1038 int scale_y;
1039
1040 scale_x = (int) (pw * scale_factor);
1041 scale_y = (int) (ph * scale_factor);
1042 return gdk_pixbuf_scale_simple (pixbuf,
1043 scale_x,
1044 scale_y,
1045 GDK_INTERP_BILINEAR);
1046 }
1047 else
1048 {
1049 return g_object_ref (pixbuf)((__typeof__ (pixbuf)) (g_object_ref) (pixbuf));
1050 }
1051}
1052
1053static void window_clicked_cb(CtkWindow* nw, CdkEventButton* button, NotifyDaemon* daemon)
1054{
1055 if (daemon->url_clicked_lock)
1056 {
1057 daemon->url_clicked_lock = FALSE(0);
1058 return;
1059 }
1060
1061 _action_invoked_cb (nw, "default");
1062 _close_notification (daemon, NW_GET_NOTIFY_ID (nw)(((guint) (gulong) (g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_id"))))
, TRUE(!(0)), NOTIFYD_CLOSED_USER);
1063}
1064
1065static void url_clicked_cb(CtkWindow* nw, const char *url)
1066{
1067 NotifyDaemon* daemon;
1068 gchar *escaped_url;
1069 gchar *cmd = NULL((void*)0);
1070 gchar *found = NULL((void*)0);
1071
1072 daemon = NW_GET_DAEMON(nw)(g_object_get_data(((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
), "_notify_daemon"))
;
1073
1074 /* Somewhat of a hack.. */
1075 daemon->url_clicked_lock = TRUE(!(0));
1076
1077 escaped_url = g_shell_quote (url);
1078
1079 if ((found = g_find_program_in_path ("gio")) != NULL((void*)0))
1080 {
1081 cmd = g_strdup_printf ("gio open %s", escaped_url);
1082 }
1083 else if ((found = g_find_program_in_path ("xdg-open")) != NULL((void*)0))
1084 {
1085 cmd = g_strdup_printf ("xdg-open %s", escaped_url);
1086 }
1087 else if ((found = g_find_program_in_path ("firefox")) != NULL((void*)0))
1088 {
1089 cmd = g_strdup_printf ("firefox %s", escaped_url);
1090 }
1091 else
1092 {
1093 g_warning ("Unable to find a browser.");
1094 }
1095
1096 g_free (found);
1097 g_free (escaped_url);
1098
1099 if (cmd != NULL((void*)0))
1100 {
1101 g_spawn_command_line_async (cmd, NULL((void*)0));
1102 g_free (cmd);
1103 }
1104}
1105
1106static gboolean screensaver_active(CtkWidget* nw)
1107{
1108 GError* error = NULL((void*)0);
1109 gboolean active = FALSE(0);
1110 GVariant *variant;
1111 GDBusProxy *proxy = NULL((void*)0);
1112
1113 proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION,
1114 G_DBUS_PROXY_FLAGS_NONE,
1115 NULL((void*)0),
1116 "org.freedesktop.ScreenSaver",
1117 "/",
1118 "org.freedesktop.ScreenSaver",
1119 NULL((void*)0),
1120 &error);
1121 if (proxy == NULL((void*)0)) {
1122 g_warning("Failed to get dbus connection: %s", error->message);
1123 g_error_free (error);
1124 }
1125
1126 variant = g_dbus_proxy_call_sync (proxy,
1127 "GetActive",
1128 g_variant_new ("()"),
1129 G_DBUS_CALL_FLAGS_NONE,
1130 -1,
1131 NULL((void*)0),
1132 &error);
1133 if (variant == NULL((void*)0))
1134 {
1135 //g_warning("Failed to call cafe-screensaver: %s", error->message);
1136 g_error_free (error);
1137 return active;
1138 }
1139
1140 g_variant_get (variant, "(b)", &active);
1141 g_variant_unref (variant);
1142 g_object_unref (proxy);
1143 return active;
1144}
1145
1146static gboolean fullscreen_window_exists(CtkWidget* nw)
1147{
1148 VnckScreen* vnck_screen;
1149 VnckWorkspace* vnck_workspace;
1150 GList* l;
1151
1152 vnck_screen = vnck_screen_get(CDK_SCREEN_XNUMBER(cdk_window_get_screen(ctk_widget_get_window(nw)))(cdk_x11_screen_get_screen_number (cdk_window_get_screen(ctk_widget_get_window
(nw))))
);
1153
1154 vnck_screen_force_update (vnck_screen);
1155
1156 vnck_workspace = vnck_screen_get_active_workspace (vnck_screen);
1157
1158 if (!vnck_workspace)
1159 {
1160 return FALSE(0);
1161 }
1162
1163 for (l = vnck_screen_get_windows_stacked (vnck_screen); l != NULL((void*)0); l = l->next)
1164 {
1165 VnckWindow *vnck_win = (VnckWindow *) l->data;
1166
1167 if (vnck_window_is_on_workspace (vnck_win, vnck_workspace) && vnck_window_is_fullscreen (vnck_win) && vnck_window_is_active (vnck_win))
1168 {
1169 /*
1170 * Sanity check if the window is _really_ fullscreen to
1171 * work around a bug in libvnck that doesn't get all
1172 * unfullscreen events.
1173 */
1174 int sw = vnck_screen_get_width (vnck_screen);
1175 int sh = vnck_screen_get_height (vnck_screen);
1176 int x, y, w, h;
1177
1178 vnck_window_get_geometry (vnck_win, &x, &y, &w, &h);
1179
1180 if (sw == w && sh == h)
1181 {
1182 return TRUE(!(0));
1183 }
1184 }
1185 }
1186
1187 return FALSE(0);
1188}
1189
1190static Window get_window_parent(CdkDisplay* display, Window window, Window* root)
1191{
1192 Window parent;
1193 Window* children = NULL((void*)0);
1194 guint nchildren;
1195 gboolean result;
1196
1197 cdk_x11_display_error_trap_push (display);
1198 result = XQueryTree(CDK_DISPLAY_XDISPLAY(display)(cdk_x11_display_get_xdisplay (display)), window, root, &parent, &children, &nchildren);
1199
1200 if (cdk_x11_display_error_trap_pop (display) || !result)
1201 {
1202 return None0L;
1203 }
1204
1205 if (children)
1206 {
1207 XFree(children);
1208 }
1209
1210 return parent;
1211}
1212
1213/*
1214 * Recurse over X Window and parents, up to root, and start watching them
1215 * for position changes.
1216 */
1217static void monitor_notification_source_windows(NotifyDaemon *daemon, NotifyTimeout *nt, Window source)
1218{
1219 CdkDisplay *display;
1220 Window root = None0L;
1221 Window parent;
1222
1223 display = cdk_display_get_default ();
1224
1225 /* Start monitoring events if necessary. We don't want to
1226 filter events unless we absolutely have to. */
1227 if (g_hash_table_size (daemon->monitored_window_hash) == 0)
1228 {
1229 cdk_window_add_filter (NULL((void*)0), (CdkFilterFunc) _notify_x11_filter, daemon);
1230 }
1231
1232 /* Store the window in the timeout */
1233 g_assert (nt != NULL)do { if (nt != ((void*)0)) ; else g_assertion_message_expr ((
(gchar*) 0), "daemon.c", 1233, ((const char*) (__func__)), "nt != NULL"
); } while (0)
;
1234 nt->src_window_xid = source;
1235
1236 for (parent = get_window_parent (display, source, &root); parent != None0L && root != parent; parent = get_window_parent (display, parent, &root))
1237 {
1238 XSelectInput (CDK_DISPLAY_XDISPLAY(display)(cdk_x11_display_get_xdisplay (display)), parent, StructureNotifyMask(1L<<17));
1239
1240 g_hash_table_insert(daemon->monitored_window_hash, GUINT_TO_POINTER (parent)((gpointer) (gulong) (parent)), GINT_TO_POINTER (nt->id)((gpointer) (glong) (nt->id)));
1241 }
1242}
1243
1244/* Use a source X Window ID to reposition a notification. */
1245static void sync_notification_position(NotifyDaemon* daemon, CtkWindow* nw, Window source)
1246{
1247 CdkDisplay *display;
1248 Statusint result;
1249 Window root;
1250 Window child;
1251 int x, y;
1252 unsigned int width, height;
1253 unsigned int border_width, depth;
1254
1255 display = cdk_display_get_default ();
1256
1257 cdk_x11_display_error_trap_push (display);
1258
1259 /* Get the root for this window */
1260 result = XGetGeometry(CDK_DISPLAY_XDISPLAY(display)(cdk_x11_display_get_xdisplay (display)), source, &root, &x, &y, &width, &height, &border_width, &depth);
1261
1262 if (cdk_x11_display_error_trap_pop (display) || !result)
1263 {
1264 return;
1265 }
1266
1267 /*
1268 * Now calculate the offset coordinates for the source window from
1269 * the root.
1270 */
1271 cdk_x11_display_error_trap_push (display);
1272 result = XTranslateCoordinates (CDK_DISPLAY_XDISPLAY (display)(cdk_x11_display_get_xdisplay (display)), source, root, 0, 0, &x, &y, &child);
1273 if (cdk_x11_display_error_trap_pop (display) || !result)
1274 {
1275 return;
1276 }
1277
1278 x += width / 2;
1279 y += height / 2;
1280
1281 theme_set_notification_arrow (nw, TRUE(!(0)), x, y);
1282 theme_move_notification (nw, x, y);
1283 theme_show_notification (nw);
1284
1285 /*
1286 * We need to manually queue a draw here as the default theme recalculates
1287 * its position in the draw handler and moves the window (which seems
1288 * fairly broken), so just calling move/show above isn't enough to cause
1289 * its position to be calculated.
1290 */
1291 ctk_widget_queue_draw (CTK_WIDGET (nw)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((nw)), ((ctk_widget_get_type ()))))))
);
1292}
1293
1294GQuark notify_daemon_error_quark(void)
1295{
1296 static GQuark q;
1297
1298 if (q == 0)
1299 {
1300 q = g_quark_from_static_string ("notification-daemon-error-quark");
1301 }
1302
1303 return q;
1304}
1305
1306static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object, GDBusMethodInvocation *invocation, const gchar *app_name, guint id, const gchar *icon, const gchar *summary, const gchar *body, const gchar *const *actions, GVariant *hints, gint timeout, gpointer user_data)
1307{
1308 NotifyDaemon *daemon;
1309 daemon = NOTIFY_DAEMON (user_data);
1310 NotifyTimeout* nt = NULL((void*)0);
1311 CtkWindow* nw = NULL((void*)0);
1312 GVariant* data;
1313 gboolean use_pos_data = FALSE(0);
1314 gboolean new_notification = FALSE(0);
1315 gint x = 0;
1316 gint y = 0;
1317 Window window_xid = None0L;
1318 guint return_id;
1319 char* sound_file = NULL((void*)0);
1320 gboolean sound_enabled;
1321 gboolean do_not_disturb;
1322 gint i;
1323 GdkPixbuf* pixbuf;
1324 GSettings* gsettings;
1325
1326 if (g_hash_table_size (daemon->notification_hash) > MAX_NOTIFICATIONS20)
1327 {
1328 g_dbus_method_invocation_return_error (invocation, notify_daemon_error_quark(), 1, _("Exceeded maximum number of notifications")gettext ("Exceeded maximum number of notifications"));
1329 return FALSE(0);
1330 }
1331
1332 /* Grab the settings */
1333 gsettings = g_settings_new (GSETTINGS_SCHEMA"org.cafe.NotificationDaemon");
1334 sound_enabled = g_settings_get_boolean (gsettings, GSETTINGS_KEY_SOUND_ENABLED"sound-enabled");
1335 do_not_disturb = g_settings_get_boolean (gsettings, GSETTINGS_KEY_DO_NOT_DISTURB"do-not-disturb");
1336 g_object_unref (gsettings);
1337
1338 /* If we are in do-not-disturb mode, just grab a new id and close the notification */
1339 if (do_not_disturb)
1340 {
1341 return_id = _generate_id (daemon);
1342 notify_daemon_notifications_complete_notify (object, invocation, return_id);
1343 return TRUE(!(0));
1344 }
1345
1346 if (id > 0)
1347 {
1348 nt = (NotifyTimeout *) g_hash_table_lookup (daemon->notification_hash, &id);
1349
1350 if (nt != NULL((void*)0))
1351 {
1352 nw = nt->nw;
1353 }
1354 else
1355 {
1356 id = 0;
1357 }
1358 }
1359
1360 if (nw == NULL((void*)0))
1361 {
1362 nw = theme_create_notification (url_clicked_cb);
1363 g_object_set_data (G_OBJECT (nw)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((nw)), (((GType) ((20) << (2))))))))
, "_notify_daemon", daemon);
1364 ctk_widget_realize (CTK_WIDGET (nw)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((nw)), ((ctk_widget_get_type ()))))))
);
1365 new_notification = TRUE(!(0));
1366
1367 g_signal_connect (G_OBJECT (nw), "button-release-event", G_CALLBACK (window_clicked_cb), daemon)g_signal_connect_data ((((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
)), ("button-release-event"), (((GCallback) (window_clicked_cb
))), (daemon), ((void*)0), (GConnectFlags) 0)
;
1368 g_signal_connect (G_OBJECT (nw), "destroy", G_CALLBACK (_notification_destroyed_cb), daemon)g_signal_connect_data ((((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
)), ("destroy"), (((GCallback) (_notification_destroyed_cb)))
, (daemon), ((void*)0), (GConnectFlags) 0)
;
1369 g_signal_connect (G_OBJECT (nw), "enter-notify-event", G_CALLBACK (_mouse_entered_cb), daemon)g_signal_connect_data ((((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
)), ("enter-notify-event"), (((GCallback) (_mouse_entered_cb)
)), (daemon), ((void*)0), (GConnectFlags) 0)
;
1370 g_signal_connect (G_OBJECT (nw), "leave-notify-event", G_CALLBACK (_mouse_exitted_cb), daemon)g_signal_connect_data ((((((GObject*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((nw)), (((GType) ((20) << (2)))))))
)), ("leave-notify-event"), (((GCallback) (_mouse_exitted_cb)
)), (daemon), ((void*)0), (GConnectFlags) 0)
;
1371 }
1372 else
1373 {
1374 theme_clear_notification_actions (nw);
1375 }
1376
1377 theme_set_notification_text (nw, summary, body);
1378 theme_set_notification_hints (nw, hints);
1379
1380 /*
1381 *XXX This needs to handle file URIs and all that.
1382 */
1383
1384
1385 if (g_variant_lookup(hints, "window-xid", "@u", &data))
1386 {
1387 window_xid = (Window) g_variant_get_uint32 (data);
1388 g_variant_unref(data);
1389 }
1390 /* deal with x, and y hints */
1391 else if (g_variant_lookup(hints, "x", "i", &x))
1392 {
1393 if (g_variant_lookup(hints, "y", "i", &y))
1394 {
1395 use_pos_data = TRUE(!(0));
1396 }
1397 }
1398
1399 if (g_variant_lookup(hints, "suppress-sound", "@*", &data))
1400 {
1401 if (g_variant_is_of_type (data, G_VARIANT_TYPE_BOOLEAN((const GVariantType *) "b")))
1402 {
1403 sound_enabled = !g_variant_get_boolean(data);
1404 }
1405 else if (g_variant_is_of_type (data, G_VARIANT_TYPE_INT32((const GVariantType *) "i")))
Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption
1406 {
1407 sound_enabled = (g_variant_get_int32(data) == 0);
1408 }
1409 else
1410 {
1411 g_warning ("suppress-sound is of type %s (expected bool or int)\n", g_variant_get_type_string(data));
1412 }
1413 g_variant_unref(data);
1414 }
1415
1416 if (sound_enabled)
1417 {
1418 if (g_variant_lookup(hints, "sound-file", "s", &sound_file))
1419 {
1420 if (*sound_file == '\0' || !g_file_test (sound_file, G_FILE_TEST_EXISTS))
1421 {
1422 g_free (sound_file);
1423 sound_file = NULL((void*)0);
1424 }
1425 }
1426 }
1427
1428 /* set up action buttons */
1429 for (i = 0; actions[i] != NULL((void*)0); i += 2)
1430 {
1431 if (actions[i+1] == NULL((void*)0))
1432 {
1433 g_warning ("Label not found for action %s. The protocol specifies that a label must follow an action in the actions array", actions[i]);
1434
1435 break;
1436 }
1437
1438 if (strcasecmp (actions[i], "default"))
1439 {
1440 theme_add_notification_action (nw, actions[i+1], actions[i], G_CALLBACK (_action_invoked_cb)((GCallback) (_action_invoked_cb)));
1441 }
1442 }
1443
1444 pixbuf = NULL((void*)0);
1445
1446 if (g_variant_lookup(hints, "image_data", "@(iiibiiay)", &data))
1447 {
1448 pixbuf = _notify_daemon_pixbuf_from_data_hint (data);
1449 g_variant_unref(data);
1450 }
1451 else if (g_variant_lookup(hints, "image-data", "@(iiibiiay)", &data))
1452 {
1453 pixbuf = _notify_daemon_pixbuf_from_data_hint (data);
1454 g_variant_unref(data);
1455 }
1456 else if (g_variant_lookup(hints, "image_path", "@s", &data))
1457 {
1458 const char *path = g_variant_get_string (data, NULL((void*)0));
1459 pixbuf = _notify_daemon_pixbuf_from_path (path);
1460 g_variant_unref(data);
1461 }
1462 else if (g_variant_lookup(hints, "image-path", "@s", &data))
1463 {
1464 const char *path = g_variant_get_string (data, NULL((void*)0));
1465 pixbuf = _notify_daemon_pixbuf_from_path (path);
1466 g_variant_unref(data);
1467 }
1468 else if (*icon != '\0')
1469 {
1470 pixbuf = _notify_daemon_pixbuf_from_path (icon);
1471 }
1472 else if (g_variant_lookup(hints, "icon_data", "@(iiibiiay)", &data))
1473 {
1474 g_warning("\"icon_data\" hint is deprecated, please use \"image_data\" instead");
1475 pixbuf = _notify_daemon_pixbuf_from_data_hint (data);
1476 g_variant_unref(data);
1477 }
1478
1479 if (pixbuf != NULL((void*)0))
1480 {
1481 GdkPixbuf *scaled;
1482 scaled = NULL((void*)0);
1483 scaled = _notify_daemon_scale_pixbuf (pixbuf, TRUE(!(0)));
1484 theme_set_notification_icon (nw, scaled);
1485 g_object_unref (G_OBJECT (pixbuf)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((pixbuf)), (((GType) ((20) << (2))))))))
);
1486 if (scaled != NULL((void*)0))
1487 g_object_unref (scaled);
1488 }
1489
1490 if (window_xid != None0L && !theme_get_always_stack (nw))
1491 {
1492 /*
1493 * Do nothing here if we were passed an XID; we'll call
1494 * sync_notification_position later.
1495 */
1496 }
1497 else if (use_pos_data && !theme_get_always_stack (nw))
1498 {
1499 /*
1500 * Typically, the theme engine will set its own position based on
1501 * the arrow X, Y hints. However, in case, move the notification to
1502 * that position.
1503 */
1504 theme_set_notification_arrow (nw, TRUE(!(0)), x, y);
1505 theme_move_notification (nw, x, y);
1506 }
1507 else
1508 {
1509 CdkMonitor *monitor_id;
1510 CdkDisplay *display;
1511 CdkSeat *seat;
1512 CdkDevice *pointer;
1513 CdkScreen* screen;
1514 gint x, y;
1515
1516 theme_set_notification_arrow (nw, FALSE(0), 0, 0);
1517
1518 /* If the "use-active-monitor" gsettings key is set to TRUE, then
1519 * get the monitor the pointer is at. Otherwise, get the monitor
1520 * number the user has set in gsettings. */
1521 if (g_settings_get_boolean(daemon->gsettings, GSETTINGS_KEY_USE_ACTIVE"use-active-monitor"))
1522 {
1523 display = cdk_display_get_default ();
1524 seat = cdk_display_get_default_seat (display);
1525 pointer = cdk_seat_get_pointer (seat);
1526
1527 cdk_device_get_position (pointer, &screen, &x, &y);
1528 monitor_id = cdk_display_get_monitor_at_point (cdk_screen_get_display (screen), x, y);
1529 }
1530 else
1531 {
1532 screen = cdk_display_get_default_screen(cdk_display_get_default());
1533 monitor_id = cdk_display_get_monitor (cdk_display_get_default(),
1534 g_settings_get_int(daemon->gsettings, GSETTINGS_KEY_MONITOR_NUMBER"monitor-number"));
1535 }
1536
1537 if (_ctk_get_monitor_num (monitor_id) >= daemon->screen->n_stacks)
1538 {
1539 /* screw it - dump it on the last one we'll get
1540 a monitors-changed signal soon enough*/
1541 monitor_id = cdk_display_get_monitor (cdk_display_get_default(), daemon->screen->n_stacks - 1);
1542 }
1543
1544 notify_stack_add_window (daemon->screen->stacks[_ctk_get_monitor_num (monitor_id)], nw, new_notification);
1545 }
1546
1547 if (id == 0)
1548 {
1549 nt = _store_notification (daemon, nw, timeout);
1550 return_id = nt->id;
1551 }
1552 else
1553 {
1554 return_id = id;
1555 }
1556
1557 /*
1558 * If we have a source Window XID, start monitoring the tree
1559 * for changes, and reposition the window based on the source
1560 * window. We need to do this after return_id is calculated.
1561 */
1562 if (window_xid != None0L && !theme_get_always_stack (nw))
1563 {
1564 monitor_notification_source_windows (daemon, nt, window_xid);
1565 sync_notification_position (daemon, nw, window_xid);
1566 }
1567
1568 /* If there is no timeout, show the notification also if screensaver
1569 * is active or there are fullscreen windows
1570 */
1571 if (!nt->has_timeout || (!screensaver_active (CTK_WIDGET (nw)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((nw)), ((ctk_widget_get_type ()))))))
) && !fullscreen_window_exists (CTK_WIDGET (nw)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((nw)), ((ctk_widget_get_type ()))))))
)))
1572 {
1573 theme_show_notification (nw);
1574
1575 if (sound_file != NULL((void*)0))
1576 {
1577 sound_play_file (CTK_WIDGET (nw)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((nw)), ((ctk_widget_get_type ()))))))
, sound_file);
1578 }
1579 }
1580 else
1581 {
1582 _NotifyPendingClose* data;
1583
1584 /* The notification was not shown, so queue up a close
1585 * for it */
1586 data = g_new0 (_NotifyPendingClose, 1)((_NotifyPendingClose *) g_malloc0_n ((1), sizeof (_NotifyPendingClose
)))
;
1587 data->id = id;
1588 data->daemon = g_object_ref (daemon)((__typeof__ (daemon)) (g_object_ref) (daemon));
1589 g_idle_add ((GSourceFunc) _close_notification_not_shown, data);
1590 }
1591
1592 g_free (sound_file);
1593
1594 g_object_set_data (G_OBJECT (nw)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((nw)), (((GType) ((20) << (2))))))))
, "_notify_id", GUINT_TO_POINTER (return_id)((gpointer) (gulong) (return_id)));
1595
1596 if (nt)
1597 {
1598 _calculate_timeout (daemon, nt, timeout);
1599 }
1600
1601 notify_daemon_notifications_complete_notify ( object, invocation, return_id);
1602 return TRUE(!(0));
1603}
1604
1605static gboolean notify_daemon_close_notification_handler(NotifyDaemonNotifications *object, GDBusMethodInvocation *invocation, guint id, gpointer user_data)
1606{
1607 if (id == 0)
1608 {
1609 g_dbus_method_invocation_return_error (invocation, notify_daemon_error_quark(), 100, _("%u is not a valid notification ID")gettext ("%u is not a valid notification ID"), id);
1610 return FALSE(0);
1611 }
1612 else
1613 {
1614 NotifyDaemon *daemon;
1615 daemon = NOTIFY_DAEMON (user_data);
1616 _close_notification (daemon, id, TRUE(!(0)), NOTIFYD_CLOSED_API);
1617 notify_daemon_notifications_complete_close_notification (object, invocation);
1618 return TRUE(!(0));
1619 }
1620}
1621
1622static gboolean notify_daemon_get_capabilities( NotifyDaemonNotifications *object, GDBusMethodInvocation *invocation)
1623{
1624 GVariantBuilder *builder;
1625 GVariant *value;
1626
1627 builder = g_variant_builder_new (G_VARIANT_TYPE ("as")(g_variant_type_checked_ (("as"))));
1628 g_variant_builder_add (builder, "s", "actions");
1629 g_variant_builder_add (builder, "s", "action-icons");
1630 g_variant_builder_add (builder, "s", "body");
1631 g_variant_builder_add (builder, "s", "body-hyperlinks");
1632 g_variant_builder_add (builder, "s", "body-markup");
1633 g_variant_builder_add (builder, "s", "icon-static");
1634 g_variant_builder_add (builder, "s", "sound");
1635 value = g_variant_new ("as", builder);
1636 g_variant_builder_unref (builder);
1637 notify_daemon_notifications_complete_get_capabilities (
1638 object,
1639 invocation,
1640 (const gchar* const *)g_variant_dup_strv (value, NULL((void*)0)));
1641 g_variant_unref (value);
1642 return TRUE(!(0));
1643}
1644
1645static gboolean notify_daemon_get_server_information (NotifyDaemonNotifications *object, GDBusMethodInvocation *invocation, gpointer user_data)
1646{
1647 notify_daemon_notifications_complete_get_server_information(object,
1648 invocation,
1649 g_strdup("Notification Daemon")g_strdup_inline ("Notification Daemon"),
1650 g_strdup("CAFE")g_strdup_inline ("CAFE"),
1651 g_strdup(PACKAGE_VERSION)g_strdup_inline ("1.25.0"),
1652 g_strdup("1.1")g_strdup_inline ("1.1"));
1653 return TRUE(!(0));
1654}
1655
1656NotifyDaemon* notify_daemon_new (gboolean replace)
1657{
1658 return g_object_new (NOTIFY_TYPE_DAEMON(notify_daemon_get_type()), "replace", replace, NULL((void*)0));
1659}