summaryrefslogtreecommitdiffstats
path: root/src/daemon/stack.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon/stack.c')
-rw-r--r--src/daemon/stack.c278
1 files changed, 278 insertions, 0 deletions
diff --git a/src/daemon/stack.c b/src/daemon/stack.c
new file mode 100644
index 0000000..8a07c82
--- /dev/null
+++ b/src/daemon/stack.c
@@ -0,0 +1,278 @@
+/*
+ * stack.c - Notification stack groups.
+ *
+ * Copyright (C) 2006 Christian Hammond <chipx86@chipx86.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include "config.h"
+#include "engines.h"
+#include "stack.h"
+
+#include <X11/Xproto.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <gdk/gdkx.h>
+
+struct _NotifyStack
+{
+ NotifyDaemon *daemon;
+ GdkScreen *screen;
+ guint monitor;
+ NotifyStackLocation location;
+ GSList *windows;
+};
+
+static gboolean
+get_work_area(GtkWidget *nw, GdkRectangle *rect)
+{
+ Atom workarea = XInternAtom(GDK_DISPLAY(), "_NET_WORKAREA", True);
+ Atom type;
+ Window win;
+ int format;
+ gulong num, leftovers;
+ gulong max_len = 4 * 32;
+ guchar *ret_workarea;
+ long *workareas;
+ int result;
+ GdkScreen *screen;
+ int disp_screen;
+
+ gtk_widget_realize(nw);
+ screen = gdk_drawable_get_screen(GDK_DRAWABLE(nw->window));
+ disp_screen = GDK_SCREEN_XNUMBER(screen);
+
+ /* Defaults in case of error */
+ rect->x = 0;
+ rect->y = 0;
+ rect->width = gdk_screen_get_width(screen);
+ rect->height = gdk_screen_get_height(screen);
+
+ if (workarea == None)
+ return FALSE;
+
+ win = XRootWindow(GDK_DISPLAY(), disp_screen);
+ result = XGetWindowProperty(GDK_DISPLAY(), win, workarea, 0,
+ max_len, False, AnyPropertyType,
+ &type, &format, &num, &leftovers,
+ &ret_workarea);
+
+ if (result != Success || type == None || format == 0 || leftovers ||
+ num % 4)
+ {
+ return FALSE;
+ }
+
+ workareas = (long *)ret_workarea;
+ rect->x = workareas[disp_screen * 4];
+ rect->y = workareas[disp_screen * 4 + 1];
+ rect->width = workareas[disp_screen * 4 + 2];
+ rect->height = workareas[disp_screen * 4 + 3];
+
+ XFree(ret_workarea);
+
+ return TRUE;
+}
+
+static void
+get_origin_coordinates(NotifyStackLocation stack_location,
+ GdkRectangle *workarea,
+ gint *x, gint *y, gint *shiftx, gint *shifty,
+ gint width, gint height)
+{
+ switch (stack_location)
+ {
+ case NOTIFY_STACK_LOCATION_TOP_LEFT:
+ *x = workarea->x;
+ *y = workarea->y;
+ *shifty = height;
+ break;
+
+ case NOTIFY_STACK_LOCATION_TOP_RIGHT:
+ *x = workarea->x + workarea->width - width;
+ *y = workarea->y;
+ *shifty = height;
+ break;
+
+ case NOTIFY_STACK_LOCATION_BOTTOM_LEFT:
+ *x = workarea->x;
+ *y = workarea->y + workarea->height - height;
+ break;
+
+ case NOTIFY_STACK_LOCATION_BOTTOM_RIGHT:
+ *x = workarea->x + workarea->width - width;
+ *y = workarea->y + workarea->height - height;
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void
+translate_coordinates(NotifyStackLocation stack_location,
+ GdkRectangle *workarea,
+ gint *x, gint *y, gint *shiftx, gint *shifty,
+ gint width, gint height, gint index)
+{
+ switch (stack_location)
+ {
+ case NOTIFY_STACK_LOCATION_TOP_LEFT:
+ *x = workarea->x;
+ *y += *shifty;
+ *shifty = height;
+ break;
+
+ case NOTIFY_STACK_LOCATION_TOP_RIGHT:
+ *x = workarea->x + workarea->width - width;
+ *y += *shifty;
+ *shifty = height;
+ break;
+
+ case NOTIFY_STACK_LOCATION_BOTTOM_LEFT:
+ *x = workarea->x;
+ *y -= height;
+ break;
+
+ case NOTIFY_STACK_LOCATION_BOTTOM_RIGHT:
+ *x = workarea->x + workarea->width - width;
+ *y -= height;
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+}
+
+NotifyStack *
+notify_stack_new(NotifyDaemon *daemon,
+ GdkScreen *screen,
+ guint monitor,
+ NotifyStackLocation location)
+{
+ NotifyStack *stack;
+
+ g_assert(daemon != NULL);
+ g_assert(screen != NULL && GDK_IS_SCREEN(screen));
+ g_assert(monitor < gdk_screen_get_n_monitors(screen));
+ g_assert(location != NOTIFY_STACK_LOCATION_UNKNOWN);
+
+ stack = g_new0(NotifyStack, 1);
+ stack->daemon = daemon;
+ stack->screen = screen;
+ stack->monitor = monitor;
+ stack->location = location;
+
+ return stack;
+}
+
+void
+notify_stack_destroy(NotifyStack *stack)
+{
+ g_assert(stack != NULL);
+
+ g_slist_free(stack->windows);
+ g_free(stack);
+}
+
+void
+notify_stack_set_location(NotifyStack *stack,
+ NotifyStackLocation location)
+{
+ stack->location = location;
+}
+
+static void
+notify_stack_shift_notifications(NotifyStack *stack,
+ GtkWindow *nw,
+ GSList **nw_l,
+ gint init_width,
+ gint init_height,
+ gint *nw_x,
+ gint *nw_y)
+{
+ GdkRectangle workarea;
+ GSList *l;
+ gint x, y, shiftx = 0, shifty = 0, index = 1;
+
+ get_work_area(GTK_WIDGET(nw), &workarea);
+ get_origin_coordinates(stack->location, &workarea, &x, &y,
+ &shiftx, &shifty, init_width, init_height);
+
+ if (nw_x != NULL)
+ *nw_x = x;
+
+ if (nw_y != NULL)
+ *nw_y = y;
+
+ for (l = stack->windows; l != NULL; l = l->next)
+ {
+ GtkWindow *nw2 = GTK_WINDOW(l->data);
+ GtkRequisition req;
+
+ if (nw2 != nw)
+ {
+ gtk_widget_size_request(GTK_WIDGET(nw2), &req);
+
+ translate_coordinates(stack->location, &workarea, &x, &y,
+ &shiftx, &shifty, req.width, req.height,
+ index++);
+ theme_move_notification(nw2, x, y);
+ }
+ else if (nw_l != NULL)
+ {
+ *nw_l = l;
+ }
+ }
+}
+
+void
+notify_stack_add_window(NotifyStack *stack,
+ GtkWindow *nw,
+ gboolean new_notification)
+{
+ GtkRequisition req;
+ gint x, y;
+
+ gtk_widget_size_request(GTK_WIDGET(nw), &req);
+ notify_stack_shift_notifications(stack, nw, NULL,
+ req.width, req.height, &x, &y);
+ theme_move_notification(nw, x, y);
+
+ if (new_notification)
+ {
+ g_signal_connect_swapped(G_OBJECT(nw), "destroy",
+ G_CALLBACK(notify_stack_remove_window),
+ stack);
+ stack->windows = g_slist_prepend(stack->windows, nw);
+ }
+}
+
+void
+notify_stack_remove_window(NotifyStack *stack,
+ GtkWindow *nw)
+{
+ GSList *remove_l = NULL;
+
+ notify_stack_shift_notifications(stack, nw, &remove_l, 0, 0, NULL, NULL);
+
+ if (remove_l != NULL)
+ stack->windows = g_slist_delete_link(stack->windows, remove_l);
+
+ if (GTK_WIDGET_REALIZED(GTK_WIDGET(nw)))
+ gtk_widget_unrealize(GTK_WIDGET(nw));
+}