Bug Summary

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