summaryrefslogtreecommitdiff
path: root/libsn
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2002-10-20 17:49:54 +0000
committerHavoc Pennington <hp@redhat.com>2002-10-20 17:49:54 +0000
commit58144125e0fe2b43bcb6aad75f258c8042b70feb (patch)
treed9008a50edad4eaf1a6e2e8c3deb7f899d998ba6 /libsn
downloadstartup-notification-58144125e0fe2b43bcb6aad75f258c8042b70feb.tar.gz
initial copy of liblf with files renamed to libsn
Diffstat (limited to 'libsn')
-rw-r--r--libsn/Makefile.am31
-rw-r--r--libsn/sn-common.c208
-rw-r--r--libsn/sn-common.h68
-rw-r--r--libsn/sn-internals.c0
-rw-r--r--libsn/sn-internals.h76
-rw-r--r--libsn/sn-launchee.c222
-rw-r--r--libsn/sn-launchee.h52
-rw-r--r--libsn/sn-launcher.c1075
-rw-r--r--libsn/sn-launcher.h103
-rw-r--r--libsn/sn-list.c168
-rw-r--r--libsn/sn-list.h55
-rw-r--r--libsn/sn-monitor.c1237
-rw-r--r--libsn/sn-monitor.h97
-rw-r--r--libsn/sn-util.c310
-rw-r--r--libsn/sn-util.h97
-rw-r--r--libsn/sn-xmessages.c836
-rw-r--r--libsn/sn-xmessages.h60
-rw-r--r--libsn/sn-xutils.c466
-rw-r--r--libsn/sn-xutils.h96
19 files changed, 5257 insertions, 0 deletions
diff --git a/libsn/Makefile.am b/libsn/Makefile.am
new file mode 100644
index 0000000..bfe4e45
--- /dev/null
+++ b/libsn/Makefile.am
@@ -0,0 +1,31 @@
+
+INCLUDES=-I$(top_srcdir) $(LIBLF_CFLAGS)
+
+liblfincludedir=$(includedir)/liblf-1.0/liblf
+
+lib_LTLIBRARIES=liblf-1.la
+
+liblfinclude_HEADERS= \
+ lf.h \
+ lf-common.h \
+ lf-launchee.h \
+ lf-launcher.h \
+ lf-monitor.h \
+ lf-util.h
+
+liblf_1_la_SOURCES= \
+ lf-common.c \
+ lf-internals.c \
+ lf-internals.h \
+ lf-launchee.c \
+ lf-launcher.c \
+ lf-list.c \
+ lf-list.h \
+ lf-monitor.c \
+ lf-util.c \
+ lf-xmessages.c \
+ lf-xmessages.h \
+ lf-xutils.c \
+ lf-xutils.h
+
+liblf_1_la_LIBADD= $(LIBLF_LIBS)
diff --git a/libsn/sn-common.c b/libsn/sn-common.c
new file mode 100644
index 0000000..f14141f
--- /dev/null
+++ b/libsn/sn-common.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <config.h>
+#include "lf-common.h"
+#include "lf-internals.h"
+
+struct LfDisplay
+{
+ int refcount;
+ Display *xdisplay;
+ int n_screens;
+ Screen **screens;
+ LfDisplayErrorTrapPush push_trap_func;
+ LfDisplayErrorTrapPop pop_trap_func;
+};
+
+/**
+ * lf_display_new:
+ * @xdisplay: an X window system display
+ * @push_trap_func: function to push an X error trap
+ * @pop_trap_func: function to pop an X error trap
+ *
+ * Creates a new #LfDisplay object, containing
+ * data that liblf associates with an X display.
+ *
+ * @push_trap_func should be a function that causes X errors to be
+ * ignored until @pop_trap_func is called as many times as
+ * @push_trap_func has been called. (Nested push/pop pairs must be
+ * supported.) The outermost @pop_trap_func in a set of nested pairs
+ * must call XSync() to ensure that all errors that will occur have in
+ * fact occurred. These functions are used to avoid X errors due to
+ * BadWindow and such.
+ *
+ * Return value: the new #LfDisplay
+ **/
+LfDisplay*
+lf_display_new (Display *xdisplay,
+ LfDisplayErrorTrapPush push_trap_func,
+ LfDisplayErrorTrapPop pop_trap_func)
+{
+ LfDisplay *display;
+ int i;
+
+ display = lf_new0 (LfDisplay, 1);
+
+ display->xdisplay = xdisplay;
+ display->n_screens = ScreenCount (xdisplay);
+ display->screens = lf_new (Screen*, display->n_screens);
+ display->refcount = 1;
+
+ display->push_trap_func = push_trap_func;
+ display->pop_trap_func = pop_trap_func;
+
+ for (i = 0; i < display->n_screens; ++i)
+ display->screens[i] = ScreenOfDisplay (display->xdisplay, i);
+
+ return display;
+}
+
+/**
+ * lf_display_ref:
+ * @display: an #LfDisplay
+ *
+ * Increment the reference count for @display
+ **/
+void
+lf_display_ref (LfDisplay *display)
+{
+ display->refcount += 1;
+}
+
+/**
+ * lf_display_unref:
+ * @display: an #LfDisplay
+ *
+ * Decrement the reference count for @display, freeing
+ * display if the reference count reaches zero.
+ **/
+void
+lf_display_unref (LfDisplay *display)
+{
+ display->refcount -= 1;
+ if (display->refcount == 0)
+ {
+ lf_free (display->screens);
+ lf_free (display);
+ }
+}
+
+/**
+ * lf_display_get_x_display:
+ * @display: an #LfDisplay
+ *
+ *
+ *
+ * Return value: X display for this #LfDisplay
+ **/
+Display*
+lf_display_get_x_display (LfDisplay *display)
+{
+
+ return display->xdisplay;
+}
+
+/**
+ * lf_display_get_x_screen:
+ * @display: an #LfDisplay
+ * @number: screen number to get
+ *
+ * Gets a screen by number; if the screen number
+ * does not exist, returns %NULL.
+ *
+ * Return value: X screen or %NULL
+ **/
+Screen*
+lf_display_get_x_screen (LfDisplay *display,
+ int number)
+{
+ if (number < 0 || number >= display->n_screens)
+ return NULL;
+ else
+ return display->screens[number];
+}
+
+/**
+ * lf_display_process_event:
+ * @display: a display
+ * @xevent: X event
+ *
+ * liblf should be given a chance to see all X events by passing them
+ * to this function. If the event was a property notify or client
+ * message related to the launch feedback protocol, the
+ * lf_display_process_event() returns true. Calling
+ * lf_display_process_event() is not currently required for launchees,
+ * only launchers and launch feedback displayers. The function returns
+ * false for mapping, unmapping, window destruction, and selection
+ * events even if they were involved in launch feedback.
+ *
+ * Return value: true if the event was a property notify or client message involved in launch feedback
+ **/
+lf_bool_t
+lf_display_process_event (LfDisplay *display,
+ XEvent *xevent)
+{
+ lf_bool_t retval;
+
+ retval = FALSE;
+
+ if (lf_internal_launcher_process_event (display, xevent))
+ retval = TRUE;
+
+ if (lf_internal_monitor_process_event (display, xevent))
+ retval = TRUE;
+
+ if (lf_internal_xmessage_process_event (display, xevent))
+ retval = TRUE;
+
+ return retval;
+}
+
+/**
+ * lf_display_error_trap_push:
+ * @display: a display
+ *
+ * Calls the push_trap_func from lf_display_new() if non-NULL.
+ **/
+void
+lf_display_error_trap_push (LfDisplay *display)
+{
+ if (display->push_trap_func)
+ (* display->push_trap_func) (display, display->xdisplay);
+}
+
+/**
+ * lf_display_error_trap_pop:
+ * @display: a display
+ *
+ * Calls the pop_trap_func from lf_display_new() if non-NULL.
+ **/
+void
+lf_display_error_trap_pop (LfDisplay *display)
+{
+ if (display->pop_trap_func)
+ (* display->pop_trap_func) (display, display->xdisplay);
+}
+
diff --git a/libsn/sn-common.h b/libsn/sn-common.h
new file mode 100644
index 0000000..57a1ba8
--- /dev/null
+++ b/libsn/sn-common.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+#ifndef __LF_COMMON_H__
+#define __LF_COMMON_H__
+
+#include <liblf/lf-util.h>
+#include <X11/Xlib.h>
+
+LF_BEGIN_DECLS
+
+typedef struct LfDisplay LfDisplay;
+
+typedef enum
+{
+ LF_LAUNCH_TYPE_OTHER,
+ LF_LAUNCH_TYPE_DOCK_ICON,
+ LF_LAUNCH_TYPE_DESKTOP_ICON,
+ LF_LAUNCH_TYPE_MENU,
+ LF_LAUNCH_TYPE_KEY_SHORTCUT
+
+} LfLaunchType;
+
+typedef void (* LfDisplayErrorTrapPush) (LfDisplay *display,
+ Display *xdisplay);
+typedef void (* LfDisplayErrorTrapPop) (LfDisplay *display,
+ Display *xdisplay);
+
+LfDisplay* lf_display_new (Display *xdisplay,
+ LfDisplayErrorTrapPush push_trap_func,
+ LfDisplayErrorTrapPop pop_trap_func);
+void lf_display_ref (LfDisplay *display);
+void lf_display_unref (LfDisplay *display);
+Display* lf_display_get_x_display (LfDisplay *display);
+Screen* lf_display_get_x_screen (LfDisplay *display,
+ int number);
+lf_bool_t lf_display_process_event (LfDisplay *display,
+ XEvent *xevent);
+void lf_display_error_trap_push (LfDisplay *display);
+void lf_display_error_trap_pop (LfDisplay *display);
+
+
+
+LF_END_DECLS
+
+#endif /* __LF_COMMON_H__ */
diff --git a/libsn/sn-internals.c b/libsn/sn-internals.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libsn/sn-internals.c
diff --git a/libsn/sn-internals.h b/libsn/sn-internals.h
new file mode 100644
index 0000000..4500e19
--- /dev/null
+++ b/libsn/sn-internals.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+#ifndef __LF_INTERNALS_H__
+#define __LF_INTERNALS_H__
+
+#include <liblf/lf-common.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <liblf/lf-list.h>
+#include <liblf/lf-xutils.h>
+
+LF_BEGIN_DECLS
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef NULL
+#define NULL ((void*) 0)
+#endif
+
+/* --- From lf-launcher.c --- */
+lf_bool_t lf_internal_launcher_process_event (LfDisplay *display,
+ XEvent *xevent);
+
+/* --- From lf-monitor.c --- */
+lf_bool_t lf_internal_monitor_process_event (LfDisplay *display,
+ XEvent *xevent);
+
+/* --- From lf-util.c --- */
+lf_bool_t lf_internal_utf8_validate (const char *str,
+ int max_len);
+char* lf_internal_strdup (const char *str);
+char* lf_internal_strndup (const char *str,
+ int n);
+void lf_internal_strfreev (char **strings);
+
+unsigned long lf_internal_string_to_ulong (const char* str);
+
+/* --- From lf-xmessages.c --- */
+lf_bool_t lf_internal_xmessage_process_event (LfDisplay *display,
+ XEvent *xevent);
+
+LF_END_DECLS
+
+#endif /* __LF_INTERNALS_H__ */
diff --git a/libsn/sn-launchee.c b/libsn/sn-launchee.c
new file mode 100644
index 0000000..85e5e43
--- /dev/null
+++ b/libsn/sn-launchee.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "lf-launchee.h"
+#include "lf-internals.h"
+#include <errno.h>
+
+struct LfLauncheeContext
+{
+ int refcount;
+ LfDisplay *display;
+ char *launch_id;
+ Window launch_window;
+};
+
+/**
+ * lf_launchee_context_new:
+ * @display: an #LfDisplay
+ * @launch_id: launch ID as in DESKTOP_LAUNCH_ID
+ * @launch_window: launch window as in DESKTOP_LAUNCH_WINDOW
+ *
+ * Creates a new launchee-side context for the launch feedback
+ * protocol.
+ *
+ * Return value: a new launchee context
+ **/
+LfLauncheeContext*
+lf_launchee_context_new (LfDisplay *display,
+ const char *launch_id,
+ Window launch_window)
+{
+ LfLauncheeContext *context;
+
+ context = lf_new0 (LfLauncheeContext, 1);
+
+ context->refcount = 1;
+
+ context->display = display;
+ lf_display_ref (context->display);
+
+ context->launch_id = lf_malloc (strlen (launch_id) + 1);
+ strcpy (context->launch_id, launch_id);
+
+ context->launch_window = launch_window;
+
+ return context;
+}
+
+/**
+ * lf_launchee_context_new_from_environment:
+ * @display: an #LfDisplay
+ *
+ * Tries to create an #LfLauncheeContext given information in
+ * the program's environment (DESKTOP_LAUNCH_ID and DESKTOP_LAUNCH_WINDOW
+ * environment variables). Returns %NULL if the env variables are not
+ * available or can't be parsed.
+ *
+ * Return value: a new #LfLauncheeContext or %NULL
+ **/
+LfLauncheeContext*
+lf_launchee_context_new_from_environment (LfDisplay *display)
+{
+ const char *id_str;
+ const char *window_str;
+ unsigned long window;
+
+ id_str = getenv ("DESKTOP_LAUNCH_ID");
+ window_str = getenv ("DESKTOP_LAUNCH_WINDOW");
+
+ if (id_str == NULL || window_str == NULL)
+ return NULL;
+
+ window = lf_internal_string_to_ulong (window_str);
+
+ if (window == None)
+ return NULL;
+
+ return lf_launchee_context_new (display, id_str, window);
+}
+
+void
+lf_launchee_context_ref (LfLauncheeContext *context)
+{
+ context->refcount += 1;
+}
+
+void
+lf_launchee_context_unref (LfLauncheeContext *context)
+{
+ context->refcount -= 1;
+ if (context->refcount == 0)
+ {
+ lf_free (context->launch_id);
+
+ lf_free (context);
+ }
+}
+
+Window
+lf_launchee_context_get_launch_window (LfLauncheeContext *context)
+{
+ return context->launch_window;
+}
+
+const char*
+lf_launchee_context_get_launch_id (LfLauncheeContext *context)
+{
+ return context->launch_id;
+}
+
+/**
+ * lf_launchee_context_pulse:
+ * @context: an #LfLauncheeContext
+ *
+ * Notifies the launcher that progress is being made. Should be
+ * called regularly during a long launch operation.
+ *
+ **/
+void
+lf_launchee_context_pulse (LfLauncheeContext *context)
+{
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.display = lf_display_get_x_display (context->display);
+ xev.xclient.window = context->launch_window;
+ xev.xclient.message_type = lf_internal_atom_get (context->display,
+ "_NET_LAUNCH_PULSE");
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 0;
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+
+ lf_display_error_trap_push (context->display);
+ XSendEvent (lf_display_get_x_display (context->display),
+ context->launch_window,
+ False,
+ PropertyChangeMask,
+ &xev);
+ XFlush (lf_display_get_x_display (context->display));
+ lf_display_error_trap_pop (context->display);
+}
+
+/**
+ * lf_launchee_context_cancel:
+ * @context: an #LfLauncheeContext
+ *
+ * Called by the launchee application to cancel a launch (will
+ * probably cause the launcher to kill the launchee).
+ *
+ **/
+void
+lf_launchee_context_cancel (LfLauncheeContext *context)
+{
+ lf_internal_set_cardinal (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_CANCELED",
+ 0);
+}
+
+/**
+ * lf_launchee_context_complete:
+ * @context: an #LfLauncheeContext
+ *
+ * Called by the launchee application when it is fully started up
+ * and launch feedback should end.
+ *
+ **/
+void
+lf_launchee_context_complete (LfLauncheeContext *context)
+{
+ lf_internal_set_cardinal (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_COMPLETE",
+ 0);
+}
+
+/**
+ * lf_launchee_context_setup_window:
+ * @context: a #LfLauncheeContext
+ * @xwindow: window to be set up
+ *
+ * Sets up @xwindow, marking it as launched by the launch sequence
+ * represented by @context. For example sets the _NET_LAUNCH_ID
+ * property. Only the group leader windows of an application
+ * MUST be set up with this function, though if
+ * any other windows come from a separate launch sequence,
+ * they can be setup up separately.
+ *
+ **/
+void
+lf_launchee_context_setup_window (LfLauncheeContext *context,
+ Window xwindow)
+{
+ lf_internal_set_string (context->display,
+ xwindow,
+ "_NET_LAUNCH_ID",
+ context->launch_id);
+}
diff --git a/libsn/sn-launchee.h b/libsn/sn-launchee.h
new file mode 100644
index 0000000..4969dc3
--- /dev/null
+++ b/libsn/sn-launchee.h
@@ -0,0 +1,52 @@
+/* Launchee API - if you are a program started by other programs */
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+#ifndef __LF_LAUNCHEE_H__
+#define __LF_LAUNCHEE_H__
+
+#include <liblf/lf-common.h>
+
+LF_BEGIN_DECLS
+
+typedef struct LfLauncheeContext LfLauncheeContext;
+
+LfLauncheeContext* lf_launchee_context_new (LfDisplay *display,
+ const char *launch_id,
+ Window launch_window);
+LfLauncheeContext* lf_launchee_context_new_from_environment (LfDisplay *display);
+void lf_launchee_context_ref (LfLauncheeContext *context);
+void lf_launchee_context_unref (LfLauncheeContext *context);
+Window lf_launchee_context_get_launch_window (LfLauncheeContext *context);
+const char* lf_launchee_context_get_launch_id (LfLauncheeContext *context);
+void lf_launchee_context_pulse (LfLauncheeContext *context);
+void lf_launchee_context_cancel (LfLauncheeContext *context);
+void lf_launchee_context_complete (LfLauncheeContext *context);
+void lf_launchee_context_setup_window (LfLauncheeContext *context,
+ Window xwindow);
+
+LF_END_DECLS
+
+#endif /* __LF_LAUNCHEE_H__ */
diff --git a/libsn/sn-launcher.c b/libsn/sn-launcher.c
new file mode 100644
index 0000000..f9803ff
--- /dev/null
+++ b/libsn/sn-launcher.c
@@ -0,0 +1,1075 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "lf-launcher.h"
+#include "lf-internals.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+static LfList* context_list = NULL;
+
+struct LfLauncherContext
+{
+ int refcount;
+ LfDisplay *display;
+ LfLauncherEventFunc event_func;
+ void *event_func_data;
+ LfFreeFunc free_data_func;
+ char *launch_id;
+ Window launch_window;
+ LfLaunchType type;
+ Window geometry_window;
+ char *name;
+ char *description;
+ int workspace;
+ char *resource_class;
+ char *resource_name;
+ char *window_title;
+ char *binary_name;
+ int pid;
+ char *icon_name;
+ int x, y, width, height;
+ unsigned int supports_cancel : 1;
+ unsigned int completed : 1;
+ unsigned int canceled : 1;
+ unsigned int geometry_set : 1;
+};
+
+/**
+ * lf_launcher_context_new:
+ * @display: an #LfDisplay
+ * @event_func: function to be called when a notable event occurs
+ * @event_func_data: data to pass to @event_func
+ * @free_data_func: function to be called on @event_func_data when freeing the context
+ *
+ * Creates a new launcher context, to be used by the program that is
+ * starting a launch sequence. For example a file manager might
+ * create a launcher context when the user double-clicks on
+ * an application icon.
+ *
+ * Return value: a new #LfLauncherContext
+ **/
+LfLauncherContext*
+lf_launcher_context_new (LfDisplay *display,
+ LfLauncherEventFunc event_func,
+ void *event_func_data,
+ LfFreeFunc free_data_func)
+{
+ LfLauncherContext *context;
+
+ if (context_list == NULL)
+ context_list = lf_list_new ();
+
+ context = lf_new0 (LfLauncherContext, 1);
+
+ context->refcount = 1;
+ context->display = display;
+ lf_display_ref (context->display);
+ context->event_func = event_func;
+ context->event_func_data = event_func_data;
+ context->free_data_func = free_data_func;
+
+ context->workspace = -1;
+ context->pid = -1;
+ context->type = LF_LAUNCH_TYPE_OTHER;
+
+ lf_list_prepend (context_list, context);
+
+ return context;
+}
+
+/**
+ * lf_launcher_context_ref:
+ * @context: a #LfLauncherContext
+ *
+ * Increments the reference count of @context
+ **/
+void
+lf_launcher_context_ref (LfLauncherContext *context)
+{
+ context->refcount += 1;
+}
+
+/**
+ * lf_launcher_context_unref:
+ * @context: a #LfLauncherContext
+ *
+ * Decrements the reference count of @context and frees the
+ * context if the count reaches zero.
+ **/
+void
+lf_launcher_context_unref (LfLauncherContext *context)
+{
+ context->refcount -= 1;
+
+ if (context->refcount == 0)
+ {
+ lf_list_remove (context_list, context);
+
+ if (context->free_data_func)
+ (* context->free_data_func) (context->event_func_data);
+
+ lf_free (context->launch_id);
+
+ if (context->launch_window != None)
+ {
+ lf_display_error_trap_push (context->display);
+
+ XDestroyWindow (lf_display_get_x_display (context->display),
+ context->launch_window);
+
+ lf_display_error_trap_pop (context->display);
+ }
+
+ lf_display_unref (context->display);
+ lf_free (context);
+ }
+}
+
+static char*
+strip_slashes (const char *src)
+{
+ char *canonicalized_name;
+ char *s;
+
+ canonicalized_name = lf_internal_strdup (src);
+
+ s = canonicalized_name;
+ while (*s)
+ {
+ if (*s == '/')
+ *s = '|';
+ ++s;
+ }
+
+ return canonicalized_name;
+}
+
+/**
+ * lf_launcher_context_initiate:
+ * @context: an #LfLaunchContext
+ * @launcher_name: name of the launcher app, suitable for debug output
+ * @launchee_name: name of the launchee app, suitable for debug output
+ * @timestamp: X timestamp of event causing the launch
+ *
+ * Initiates a launch sequence. All the properties of the launch (such
+ * as type, geometry, description) should be set up prior to
+ * initiating the sequence.
+ **/
+void
+lf_launcher_context_initiate (LfLauncherContext *context,
+ const char *launcher_name,
+ const char *launchee_name,
+ Time timestamp)
+{
+ static int sequence_number = 0;
+ static lf_bool_t have_hostname = FALSE;
+ static char hostbuf[257];
+ char *s;
+ int len;
+ Display *xdisplay;
+ char *canonicalized_launcher;
+ char *canonicalized_launchee;
+ Atom atoms[1];
+
+ if (context->launch_id != NULL)
+ {
+ fprintf (stderr, "%s called twice for the same LfLaunchContext\n",
+ __FUNCTION__);
+ return;
+ }
+
+ if (!have_hostname)
+ {
+ if (gethostname (hostbuf, sizeof (hostbuf)-1) == 0)
+ have_hostname = TRUE;
+ else
+ hostbuf[0] = '\0';
+ }
+
+ canonicalized_launcher = strip_slashes (launcher_name);
+ canonicalized_launchee = strip_slashes (launchee_name);
+
+ /* man I wish we could use g_strdup_printf */
+ len = strlen (launcher_name) + strlen (launchee_name) +
+ 256; /* 256 is longer than a couple %d and some slashes */
+
+ s = lf_malloc (len + 3);
+ snprintf (s, len, "%s/%s/%lu/%d-%d-%s",
+ canonicalized_launcher, canonicalized_launchee, (unsigned long) timestamp,
+ (int) getpid (), (int) sequence_number, hostbuf);
+ ++sequence_number;
+
+ lf_free (canonicalized_launcher);
+ lf_free (canonicalized_launchee);
+
+ context->launch_id = s;
+
+ xdisplay = lf_display_get_x_display (context->display);
+
+ {
+ XSetWindowAttributes attrs;
+
+ attrs.override_redirect = True;
+ attrs.event_mask = PropertyChangeMask | StructureNotifyMask;
+
+ context->launch_window =
+ XCreateWindow (xdisplay,
+ RootWindow (xdisplay, 0),
+ -100, -100, 1, 1,
+ 0,
+ CopyFromParent,
+ CopyFromParent,
+ CopyFromParent,
+ CWOverrideRedirect | CWEventMask,
+ &attrs);
+ }
+
+ /* push outer error to allow avoiding XSync after every
+ * property set
+ */
+ lf_display_error_trap_push (context->display);
+
+ lf_internal_set_string (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_ID",
+ context->launch_id);
+
+ lf_internal_set_string (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_HOSTNAME",
+ hostbuf);
+
+ switch (context->type)
+ {
+ case LF_LAUNCH_TYPE_OTHER:
+ atoms[0] = lf_internal_atom_get (context->display,
+ "_NET_LAUNCH_TYPE_OTHER");
+ break;
+ case LF_LAUNCH_TYPE_DOCK_ICON:
+ atoms[0] = lf_internal_atom_get (context->display,
+ "_NET_LAUNCH_TYPE_DOCK_ICON");
+ break;
+ case LF_LAUNCH_TYPE_DESKTOP_ICON:
+ atoms[0] = lf_internal_atom_get (context->display,
+ "_NET_LAUNCH_TYPE_DESKTOP_ICON");
+ break;
+ case LF_LAUNCH_TYPE_MENU:
+ atoms[0] = lf_internal_atom_get (context->display,
+ "_NET_LAUNCH_TYPE_MENU");
+ break;
+ case LF_LAUNCH_TYPE_KEY_SHORTCUT:
+ atoms[0] = lf_internal_atom_get (context->display,
+ "_NET_LAUNCH_TYPE_KEY_SHORTCUT");
+ break;
+ }
+
+ lf_internal_set_atom_list (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_TYPE",
+ atoms, 1);
+
+ if (context->geometry_set)
+ {
+ int cardinals[4];
+
+ cardinals[0] = context->x;
+ cardinals[1] = context->y;
+ cardinals[2] = context->width;
+ cardinals[3] = context->height;
+
+ lf_internal_set_cardinal_list (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_GEOMETRY",
+ cardinals, 4);
+ }
+
+ if (context->geometry_window != None)
+ {
+ lf_internal_set_window (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_GEOMETRY_WINDOW",
+ context->geometry_window);
+ }
+
+ if (context->supports_cancel)
+ {
+ lf_internal_set_cardinal (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_SUPPORTS_CANCEL",
+ context->supports_cancel);
+ }
+
+ if (context->name)
+ {
+ lf_internal_set_utf8_string (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_NAME",
+ context->name);
+ }
+
+ if (context->description)
+ {
+ lf_internal_set_utf8_string (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_DESCRIPTION",
+ context->description);
+ }
+
+ if (context->workspace >= 0)
+ {
+ lf_internal_set_cardinal (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_DESKTOP",
+ context->workspace);
+ }
+
+ if (context->pid >= 0)
+ {
+ lf_internal_set_cardinal (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_PID",
+ context->pid);
+ }
+
+ if (context->binary_name)
+ {
+ lf_internal_set_string (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_BINARY_NAME",
+ context->binary_name);
+ }
+
+ if (context->icon_name)
+ {
+ lf_internal_set_string (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_ICON_NAME",
+ context->icon_name);
+ }
+
+ if (context->resource_class)
+ {
+ lf_internal_set_string (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_LEGACY_RESOURCE_CLASS",
+ context->resource_class);
+ }
+
+ if (context->resource_name)
+ {
+ lf_internal_set_string (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_LEGACY_RESOURCE_NAME",
+ context->resource_name);
+ }
+
+ if (context->window_title)
+ {
+ lf_internal_set_string (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_LEGACY_NAME",
+ context->window_title);
+ }
+
+ lf_display_error_trap_pop (context->display);
+
+ /* Sync to server (so the launch window ID exists for example) */
+ XFlush (xdisplay);
+
+ /* Initiation message to all screens */
+ {
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.display = xdisplay;
+ xev.xclient.window = context->launch_window;
+ xev.xclient.message_type = lf_internal_atom_get (context->display,
+ "_NET_LAUNCH_INITIATE");
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = timestamp;
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+
+ lf_internal_send_event_all_screens (context->display,
+ PropertyChangeMask,
+ &xev);
+ }
+}
+
+Window
+lf_launcher_context_get_launch_window (LfLauncherContext *context)
+{
+ return context->launch_window;
+}
+
+const char*
+lf_launcher_context_get_launch_id (LfLauncherContext *context)
+{
+
+ return context->launch_id;
+}
+
+lf_bool_t
+lf_launcher_context_get_initiated (LfLauncherContext *context)
+{
+ return context->launch_id != NULL;
+}
+
+lf_bool_t
+lf_launcher_context_get_canceled (LfLauncherContext *context)
+{
+ return context->canceled;
+}
+
+/**
+ * lf_launcher_context_get_completed:
+ * @context: an #LfLauncherContext
+ *
+ * Returns %TRUE if _NET_LAUNCH_COMPLETE has been set or the
+ * launch sequence window has been destroyed.
+ *
+ * Return value: %TRUE if the launch sequence has been completed
+ **/
+lf_bool_t
+lf_launcher_context_get_completed (LfLauncherContext *context)
+{
+ return context->completed;
+}
+
+/**
+ * lf_launcher_context_cancel:
+ * @context: an #LfLauncherContext
+ *
+ * Marks the launch canceled by setting the _NET_LAUNCH_CANCELED
+ * property on the launch window. May not be called if the launch has
+ * not been initiated. An #LF_LAUNCHER_EVENT_CANCELED event should be
+ * received in response to the cancellation, under normal
+ * circumstances.
+ *
+ * lf_launcher_context_cancel() should be called to request a
+ * cancellation. Normally the launcher process is the process that
+ * performs the cancellation as well, in response to an
+ * #LF_LAUNCHER_EVENT_CANCELED event.
+ *
+ **/
+void
+lf_launcher_context_cancel (LfLauncherContext *context)
+{
+ if (context->launch_id == NULL)
+ {
+ fprintf (stderr, "%s called for an LfLauncherContext that hasn't been initiated\n",
+ __FUNCTION__);
+ return;
+ }
+
+ lf_internal_set_cardinal (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_CANCELED",
+ 0);
+}
+
+/**
+ * lf_launcher_context_complete:
+ * @context: an #LfLauncherContext
+ *
+ * Marks @context as completed. Normally the launchee process marks a
+ * launch sequence completed, however the launcher has to do it
+ * if the launch is canceled.
+ *
+ **/
+void
+lf_launcher_context_complete (LfLauncherContext *context)
+{
+ if (context->launch_id == NULL)
+ {
+ fprintf (stderr, "%s called for an LfLauncherContext that hasn't been initiated\n",
+ __FUNCTION__);
+ return;
+ }
+
+ lf_internal_set_cardinal (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_COMPLETE",
+ 0);
+}
+
+/**
+ * lf_launcher_context_setup_child_process:
+ * @context: an #LfLauncherContext
+ *
+ * This function should be called after forking, but before exec(), in
+ * the child process being launched. It sets up the environment variables
+ * telling the child process about the launch ID and launch window.
+ * This function will leak the strings passed to putenv() so should
+ * only be used prior to an exec().
+ *
+ **/
+void
+lf_launcher_context_setup_child_process (LfLauncherContext *context)
+{
+ char *launch_id;
+ char *s;
+ char *launch_window;
+
+ if (context->launch_id == NULL)
+ {
+ fprintf (stderr, "%s called for an LfLauncherContext that hasn't been initiated\n",
+ __FUNCTION__);
+ return;
+ }
+
+ /* Man we need glib here */
+
+ launch_id = lf_malloc (strlen (context->launch_id) + strlen ("DESKTOP_LAUNCH_ID") + 3);
+ strcpy (launch_id, "DESKTOP_LAUNCH_ID=");
+ strcat (launch_id, context->launch_id);
+
+ putenv (launch_id);
+
+ launch_window = lf_malloc (strlen ("DESKTOP_LAUNCH_WINDOW") + 128);
+ strcpy (launch_window, "DESKTOP_LAUNCH_WINDOW=");
+ s = launch_window;
+ while (*s)
+ ++s;
+
+ snprintf (s, 100, "0x%lx", context->launch_window);
+
+ putenv (launch_window);
+
+ /* Can't free strings passed to putenv */
+}
+
+#define WARN_ALREADY_INITIATED(context) do { if ((context)->launch_id != NULL) { \
+ fprintf (stderr, "%s called for an LfLauncherContext that has already been initiated\n", \
+ __FUNCTION__); \
+ return; \
+} } while (0)
+
+void
+lf_launcher_context_set_launch_type (LfLauncherContext *context,
+ LfLaunchType type)
+{
+ WARN_ALREADY_INITIATED (context);
+
+ context->type = type;
+}
+
+void
+lf_launcher_context_set_geometry_window (LfLauncherContext *context,
+ Window xwindow)
+{
+ WARN_ALREADY_INITIATED (context);
+
+ context->geometry_window = xwindow;
+}
+
+void
+lf_launcher_context_set_supports_cancel (LfLauncherContext *context,
+ lf_bool_t supports_cancel)
+{
+ WARN_ALREADY_INITIATED (context);
+
+ context->supports_cancel = supports_cancel;
+}
+
+void
+lf_launcher_context_set_launch_name (LfLauncherContext *context,
+ const char *name)
+{
+ WARN_ALREADY_INITIATED (context);
+
+ lf_free (context->name);
+ context->name = lf_internal_strdup (name);
+}
+
+void
+lf_launcher_context_set_launch_description (LfLauncherContext *context,
+ const char *description)
+{
+ WARN_ALREADY_INITIATED (context);
+
+ lf_free (context->description);
+ context->description = lf_internal_strdup (description);
+}
+
+void
+lf_launcher_context_set_launch_workspace (LfLauncherContext *context,
+ int workspace)
+{
+ WARN_ALREADY_INITIATED (context);
+
+ context->workspace = workspace;
+}
+
+void
+lf_launcher_context_set_legacy_resource_class (LfLauncherContext *context,
+ const char *klass)
+{
+ WARN_ALREADY_INITIATED (context);
+
+ lf_free (context->resource_class);
+ context->resource_class = lf_internal_strdup (klass);
+}
+
+void
+lf_launcher_context_set_legacy_resource_name (LfLauncherContext *context,
+ const char *name)
+{
+ WARN_ALREADY_INITIATED (context);
+
+ lf_free (context->resource_name);
+ context->resource_name = lf_internal_strdup (name);
+}
+
+void
+lf_launcher_context_set_legacy_window_title (LfLauncherContext *context,
+ const char *title)
+{
+ WARN_ALREADY_INITIATED (context);
+
+ lf_free (context->window_title);
+ context->window_title = lf_internal_strdup (title);
+}
+
+void
+lf_launcher_context_set_binary_name (LfLauncherContext *context,
+ const char *name)
+{
+ WARN_ALREADY_INITIATED (context);
+
+ lf_free (context->binary_name);
+ context->binary_name = lf_internal_strdup (name);
+}
+
+void
+lf_launcher_context_set_pid (LfLauncherContext *context,
+ int pid)
+{
+ context->pid = pid;
+
+ /* set the X property if launch window already exists */
+ if (context->launch_id != NULL)
+ {
+ lf_internal_set_cardinal (context->display,
+ context->launch_window,
+ "_NET_LAUNCH_PID",
+ context->pid);
+ }
+}
+
+void
+lf_launcher_context_set_icon_name (LfLauncherContext *context,
+ const char *name)
+{
+ WARN_ALREADY_INITIATED (context);
+
+ lf_free (context->icon_name);
+ context->icon_name = lf_internal_strdup (name);
+}
+
+struct LfLauncherEvent
+{
+ int refcount;
+ LfLauncherEventType type;
+ Time timestamp;
+ LfLauncherContext *context;
+};
+
+/**
+ * lf_launcher_event_copy:
+ * @event: event to copy
+ *
+ * Creates a copy of @event, the copy has a reference count of one.
+ *
+ * Return value: a new #LfLauncherEvent that's a copy of @event
+ **/
+LfLauncherEvent*
+lf_launcher_event_copy (LfLauncherEvent *event)
+{
+ LfLauncherEvent *copy;
+
+ copy = lf_new (LfLauncherEvent, 1);
+
+ copy->refcount = 1;
+ copy->type = event->type;
+ copy->timestamp = event->timestamp;
+ copy->context = event->context;
+ if (copy->context)
+ lf_launcher_context_ref (copy->context);
+
+ return copy;
+}
+
+/**
+ * lf_launcher_event_ref:
+ * @event: a #LfLauncherEvent
+ *
+ * Increments @event's reference count.
+ **/
+void
+lf_launcher_event_ref (LfLauncherEvent *event)
+{
+ event->refcount += 1;
+}
+
+/**
+ * lf_launcher_event_unref:
+ * @event: a #LfLauncherEvent
+ *
+ * Decrements @event's reference count and frees @event
+ * if the count reaches zero.
+ **/
+void
+lf_launcher_event_unref (LfLauncherEvent *event)
+{
+ event->refcount -= 1;
+
+ if (event->refcount == 0)
+ {
+ if (event->context)
+ lf_launcher_context_unref (event->context);
+ lf_free (event);
+ }
+}
+
+/**
+ * lf_launcher_event_get_type:
+ * @event: a #LfLauncherEvent
+ *
+ * Gets the type of the launcher event.
+ *
+ * Return value: the type of event
+ **/
+LfLauncherEventType
+lf_launcher_event_get_type (LfLauncherEvent *event)
+{
+ return event->type;
+}
+
+/**
+ * lf_launcher_event_get_context:
+ * @event: a #LfLauncherEvent
+ *
+ * Gets the context associated with @event. The
+ * returned context is owned by the event, i.e.
+ * the caller of lf_launcher_event_get_context() should
+ * not unref the context.
+ *
+ * Return value: the context for this event
+ **/
+LfLauncherContext*
+lf_launcher_event_get_context (LfLauncherEvent *event)
+{
+ return event->context;
+}
+
+/**
+ * lf_launcher_event_get_time:
+ * @event: a #LfLauncherEvent
+ *
+ * Gets the X Window System timestamp associated with this launcher
+ * event.
+ *
+ * Return value: timestamp for the event, or CurrentTime if none available
+ **/
+Time
+lf_launcher_event_get_time (LfLauncherEvent *event)
+{
+ return event->timestamp;
+}
+
+static lf_bool_t
+check_cardinal_exists (LfDisplay *display,
+ Window xwindow,
+ const char *property)
+{
+ int val;
+
+ return lf_internal_get_cardinal (display, xwindow, property,
+ &val);
+}
+
+typedef struct
+{
+ LfDisplay *display;
+ Window launch_window;
+ lf_bool_t result;
+} HaveContextsData;
+
+static lf_bool_t
+have_active_contexts_foreach (void *value,
+ void *data)
+{
+ LfLauncherContext *context = value;
+ HaveContextsData *hcd = data;
+
+ if (!context->completed &&
+ context->launch_window == hcd->launch_window &&
+ lf_display_get_x_display (context->display) ==
+ lf_display_get_x_display (hcd->display))
+ {
+ hcd->result = TRUE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+typedef struct
+{
+ LfDisplay *display;
+ Window launch_window;
+ LfList *contexts;
+} FindContextsData;
+
+static lf_bool_t
+find_active_contexts_foreach (void *value,
+ void *data)
+{
+ LfLauncherContext *context = value;
+ FindContextsData *fcd = data;
+
+ if (!context->completed &&
+ context->launch_window == fcd->launch_window &&
+ lf_display_get_x_display (context->display) ==
+ lf_display_get_x_display (fcd->display))
+ lf_list_prepend (fcd->contexts, context);
+
+ return TRUE;
+}
+
+typedef struct
+{
+ LfLauncherEvent *base_event;
+ LfList *events;
+} CreateEventsData;
+
+static lf_bool_t
+create_events_foreach (void *value,
+ void *data)
+{
+ LfLauncherContext *context = value;
+ CreateEventsData *ced = data;
+ LfLauncherEvent *event;
+
+ event = lf_launcher_event_copy (ced->base_event);
+ event->context = context;
+ lf_launcher_context_ref (context);
+
+ lf_list_prepend (ced->events, event);
+
+ return TRUE;
+}
+
+static lf_bool_t
+dispatch_events_foreach (void *value,
+ void *data)
+{
+ LfLauncherEvent *event = value;
+
+ /* Filter out duplicate events and update flags */
+ switch (event->type)
+ {
+ case LF_LAUNCHER_EVENT_CANCELED:
+ if (event->context->canceled)
+ {
+ lf_launcher_event_unref (event);
+ event = NULL;
+ }
+ else
+ {
+ event->context->canceled = TRUE;
+ }
+ break;
+ case LF_LAUNCHER_EVENT_COMPLETED:
+ if (event->context->completed)
+ {
+ lf_launcher_event_unref (event);
+ event = NULL;
+ }
+ else
+ {
+ event->context->completed = TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (event)
+ {
+ if (event->context->event_func)
+ (* event->context->event_func) (event,
+ event->context->event_func_data);
+ lf_launcher_event_unref (event);
+ }
+
+ return TRUE;
+}
+
+static void
+dispatch_event (LfDisplay *display,
+ Window launch_window,
+ LfLauncherEvent *event)
+{
+ /* Find all applicable contexts, create an event for each, and send
+ * the events out.
+ */
+ FindContextsData fcd;
+ CreateEventsData ced;
+
+ fcd.display = display;
+ fcd.launch_window = launch_window;
+ fcd.contexts = lf_list_new ();
+
+ if (context_list != NULL)
+ lf_list_foreach (context_list, find_active_contexts_foreach, &fcd);
+
+ ced.base_event = event;
+ ced.events = lf_list_new ();
+ lf_list_foreach (fcd.contexts, create_events_foreach, &ced);
+
+ /* This unref's each event as it's dispatched */
+ lf_list_foreach (ced.events, dispatch_events_foreach, NULL);
+
+ lf_list_free (fcd.contexts);
+ lf_list_free (ced.events);
+}
+
+lf_bool_t
+lf_internal_launcher_process_event (LfDisplay *display,
+ XEvent *xevent)
+{
+ lf_bool_t retval;
+ LfLauncherEvent *event;
+ Window event_xwindow;
+
+ if (context_list == NULL ||
+ lf_list_empty (context_list))
+ return FALSE; /* no one cares */
+
+ event_xwindow = None;
+ event = NULL;
+ retval = FALSE;
+
+ switch (xevent->xany.type)
+ {
+ case PropertyNotify:
+ if (xevent->xproperty.atom ==
+ lf_internal_atom_get (display, "_NET_LAUNCH_CANCELED"))
+ {
+ event_xwindow = xevent->xproperty.window;
+
+ if (check_cardinal_exists (display, event_xwindow,
+ "_NET_LAUNCH_CANCELED"))
+ {
+ event = lf_new (LfLauncherEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_LAUNCHER_EVENT_CANCELED;
+ event->timestamp = xevent->xproperty.time;
+ event->context = NULL;
+ }
+
+ retval = TRUE;
+ }
+ else if (xevent->xproperty.atom ==
+ lf_internal_atom_get (display, "_NET_LAUNCH_COMPLETE"))
+ {
+ event_xwindow = xevent->xproperty.window;
+
+ if (check_cardinal_exists (display, event_xwindow,
+ "_NET_LAUNCH_COMPLETE"))
+ {
+ event = lf_new (LfLauncherEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_LAUNCHER_EVENT_COMPLETED;
+ event->timestamp = xevent->xproperty.time;
+ event->context = NULL;
+ }
+
+ retval = TRUE;
+ }
+ break;
+
+ case ClientMessage:
+ if (xevent->xclient.message_type ==
+ lf_internal_atom_get (display,
+ "_NET_LAUNCH_PULSE"))
+ {
+ event_xwindow = xevent->xclient.window;
+
+ event = lf_new (LfLauncherEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_LAUNCHER_EVENT_PULSE;
+ event->timestamp = CurrentTime;
+ event->context = NULL;
+
+ retval = TRUE;
+ }
+ break;
+
+ case DestroyNotify:
+ {
+ HaveContextsData hcd;
+ hcd.display = display;
+ hcd.launch_window = xevent->xdestroywindow.window;
+ hcd.result = FALSE;
+ if (context_list)
+ lf_list_foreach (context_list, have_active_contexts_foreach, &hcd);
+ if (hcd.result)
+ {
+ event_xwindow = hcd.launch_window;
+
+ event = lf_new (LfLauncherEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_LAUNCHER_EVENT_COMPLETED;
+ event->timestamp = CurrentTime;
+ event->context = NULL;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (event != NULL)
+ {
+ dispatch_event (display, event_xwindow, event);
+
+ lf_launcher_event_unref (event);
+ }
+
+ return retval;
+}
+
diff --git a/libsn/sn-launcher.h b/libsn/sn-launcher.h
new file mode 100644
index 0000000..5a805d6
--- /dev/null
+++ b/libsn/sn-launcher.h
@@ -0,0 +1,103 @@
+/* Launcher API - if you are a program that starts other programs */
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+#ifndef __LF_LAUNCHER_H__
+#define __LF_LAUNCHER_H__
+
+#include <liblf/lf-common.h>
+
+LF_BEGIN_DECLS
+
+typedef struct LfLauncherContext LfLauncherContext;
+typedef struct LfLauncherEvent LfLauncherEvent;
+
+typedef void (* LfLauncherEventFunc) (LfLauncherEvent *event,
+ void *user_data);
+
+typedef enum
+{
+ LF_LAUNCHER_EVENT_CANCELED,
+ LF_LAUNCHER_EVENT_COMPLETED,
+ LF_LAUNCHER_EVENT_PULSE
+} LfLauncherEventType;
+
+LfLauncherContext* lf_launcher_context_new (LfDisplay *display,
+ LfLauncherEventFunc event_func,
+ void *event_func_data,
+ LfFreeFunc free_data_func);
+void lf_launcher_context_ref (LfLauncherContext *context);
+void lf_launcher_context_unref (LfLauncherContext *context);
+
+
+void lf_launcher_context_initiate (LfLauncherContext *context,
+ const char *launcher_name,
+ const char *launchee_name,
+ Time timestamp);
+Window lf_launcher_context_get_launch_window (LfLauncherContext *context);
+const char* lf_launcher_context_get_launch_id (LfLauncherContext *context);
+lf_bool_t lf_launcher_context_get_initiated (LfLauncherContext *context);
+lf_bool_t lf_launcher_context_get_canceled (LfLauncherContext *context);
+lf_bool_t lf_launcher_context_get_completed (LfLauncherContext *context);
+void lf_launcher_context_cancel (LfLauncherContext *context);
+void lf_launcher_context_complete (LfLauncherContext *context);
+
+void lf_launcher_context_setup_child_process (LfLauncherContext *context);
+
+void lf_launcher_context_set_launch_type (LfLauncherContext *context,
+ LfLaunchType type);
+void lf_launcher_context_set_geometry_window (LfLauncherContext *context,
+ Window xwindow);
+void lf_launcher_context_set_supports_cancel (LfLauncherContext *context,
+ lf_bool_t supports_cancel);
+void lf_launcher_context_set_launch_name (LfLauncherContext *context,
+ const char *name);
+void lf_launcher_context_set_launch_description (LfLauncherContext *context,
+ const char *description);
+void lf_launcher_context_set_launch_workspace (LfLauncherContext *context,
+ int workspace);
+void lf_launcher_context_set_legacy_resource_class (LfLauncherContext *context,
+ const char *klass);
+void lf_launcher_context_set_legacy_resource_name (LfLauncherContext *context,
+ const char *name);
+void lf_launcher_context_set_legacy_window_title (LfLauncherContext *context,
+ const char *title);
+void lf_launcher_context_set_binary_name (LfLauncherContext *context,
+ const char *name);
+void lf_launcher_context_set_pid (LfLauncherContext *context,
+ int pid);
+void lf_launcher_context_set_icon_name (LfLauncherContext *context,
+ const char *name);
+
+LfLauncherEvent* lf_launcher_event_copy (LfLauncherEvent *event);
+void lf_launcher_event_ref (LfLauncherEvent *event);
+void lf_launcher_event_unref (LfLauncherEvent *event);
+LfLauncherEventType lf_launcher_event_get_type (LfLauncherEvent *event);
+LfLauncherContext* lf_launcher_event_get_context (LfLauncherEvent *event);
+Time lf_launcher_event_get_time (LfLauncherEvent *event);
+
+LF_END_DECLS
+
+#endif /* __LF_LAUNCHER_H__ */
diff --git a/libsn/sn-list.c b/libsn/sn-list.c
new file mode 100644
index 0000000..dbbd0a2
--- /dev/null
+++ b/libsn/sn-list.c
@@ -0,0 +1,168 @@
+/* List abstraction used internally */
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "lf-list.h"
+#include "lf-internals.h"
+
+typedef struct LfListNode
+{
+ void *data;
+ struct LfListNode *next;
+} LfListNode;
+
+struct LfList
+{
+ LfListNode *head;
+};
+
+static LfListNode*
+lf_list_node_alloc (void)
+{
+ return lf_new0 (LfListNode, 1);
+}
+
+LfList*
+lf_list_new (void)
+{
+ LfList *list;
+
+ list = lf_new (LfList, 1);
+ list->head = NULL;
+
+ return list;
+}
+
+void
+lf_list_free (LfList *list)
+{
+ LfListNode *node;
+
+ node = list->head;
+ while (node != NULL)
+ {
+ LfListNode *next = node->next;
+
+ lf_free (node);
+
+ node = next;
+ }
+
+ lf_free (list);
+}
+
+void
+lf_list_prepend (LfList *list,
+ void *data)
+{
+ if (list->head == NULL)
+ {
+ list->head = lf_list_node_alloc ();
+ list->head->data = data;
+ }
+ else
+ {
+ LfListNode *node;
+
+ node = lf_list_node_alloc ();
+ node->data = data;
+ node->next = list->head;
+ list->head = node;
+ }
+}
+
+void
+lf_list_append (LfList *list,
+ void *data)
+{
+ if (list->head == NULL)
+ {
+ list->head = lf_list_node_alloc ();
+ list->head->data = data;
+ }
+ else
+ {
+ LfListNode *node;
+
+ node = list->head;
+ while (node->next != NULL)
+ node = node->next;
+
+ node->next = lf_list_node_alloc ();
+ node->next->data = data;
+ }
+}
+
+void
+lf_list_remove (LfList *list,
+ void *data)
+{
+ LfListNode *node;
+ LfListNode *prev;
+
+ prev = NULL;
+ node = list->head;
+ while (node != NULL)
+ {
+ if (node->data == data)
+ {
+ if (prev)
+ prev->next = node->next;
+ else
+ list->head = node->next;
+
+ lf_free (node);
+
+ return;
+ }
+
+ prev = node;
+ node = node->next;
+ }
+}
+
+void
+lf_list_foreach (LfList *list,
+ LfListForeachFunc func,
+ void *data)
+{
+ LfListNode *node;
+
+ node = list->head;
+ while (node != NULL)
+ {
+ LfListNode *next = node->next; /* reentrancy safety */
+
+ if (!(* func) (node->data, data))
+ return;
+
+ node = next;
+ }
+}
+
+lf_bool_t
+lf_list_empty (LfList *list)
+{
+ return list->head == NULL;
+}
diff --git a/libsn/sn-list.h b/libsn/sn-list.h
new file mode 100644
index 0000000..5a519bb
--- /dev/null
+++ b/libsn/sn-list.h
@@ -0,0 +1,55 @@
+/* List abstraction used internally */
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+#ifndef __LF_LIST_H__
+#define __LF_LIST_H__
+
+#include <liblf/lf-util.h>
+
+LF_BEGIN_DECLS
+
+/* FIXME use lf_internal prefix for all this */
+
+typedef struct LfList LfList;
+
+typedef lf_bool_t (* LfListForeachFunc) (void *value, void *data);
+
+LfList* lf_list_new (void);
+void lf_list_free (LfList *list);
+void lf_list_prepend (LfList *list,
+ void *data);
+void lf_list_append (LfList *list,
+ void *data);
+void lf_list_remove (LfList *list,
+ void *data);
+void lf_list_foreach (LfList *list,
+ LfListForeachFunc func,
+ void *data);
+lf_bool_t lf_list_empty (LfList *list);
+
+LF_END_DECLS
+
+#endif /* __LF_LIST_H__ */
diff --git a/libsn/sn-monitor.c b/libsn/sn-monitor.c
new file mode 100644
index 0000000..52a6134
--- /dev/null
+++ b/libsn/sn-monitor.c
@@ -0,0 +1,1237 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "lf-monitor.h"
+#include "lf-internals.h"
+#include "lf-xmessages.h"
+
+#define KDE_STARTUP_INFO_ATOM "_KDE_STARTUP_INFO"
+
+struct LfMonitorContext
+{
+ int refcount;
+ LfDisplay *display;
+ LfMonitorEventFunc event_func;
+ void *event_func_data;
+ LfFreeFunc free_data_func;
+ /* a context doesn't get events for sequences
+ * started prior to context creation
+ */
+ int creation_serial;
+};
+
+struct LfMonitorEvent
+{
+ int refcount;
+ LfMonitorEventType type;
+ LfMonitorContext *context;
+ LfLaunchSequence *sequence;
+ Time timestamp;
+};
+
+struct LfLaunchSequence
+{
+ int refcount;
+
+ char *id;
+ LfDisplay *display;
+
+ /* launch_window is NULL for Xmessage-based launches */
+ Window launch_window;
+
+ char *name;
+ char *description;
+
+ char *resource_class;
+ char *resource_name;
+ char *window_title;
+
+ int workspace;
+
+ char *binary_name;
+ char *hostname;
+ char *icon_name;
+ int pid;
+
+ /* geometry */
+ Window geometry_window;
+ int x, y, width, height;
+
+ unsigned int geometry_set : 1;
+ unsigned int canceled : 1;
+ unsigned int completed : 1;
+ unsigned int supports_cancel : 1;
+
+ int creation_serial;
+};
+
+static LfList *context_list = NULL;
+static LfList *sequence_list = NULL;
+static int next_sequence_serial = 0;
+
+static void xmessage_func (LfDisplay *display,
+ const char *message_type,
+ const char *message,
+ void *user_data);
+
+/**
+ * lf_monitor_context_new:
+ * @display: an #LfDisplay
+ * @event_func: function to call when an event is received
+ * @event_func_data: extra data to pass to @event_func
+ * @free_data_func: function to free @event_func_data when the context is freed
+ *
+ * Creates a new context for monitoring launch sequences. Normally
+ * only launch feedback indicator applications such as the window
+ * manager or task manager would use #LfMonitorContext.
+ * #LfLauncherContext and #LfLauncheeContext are more often used by
+ * applications.
+ *
+ * To detect launch sequence initiations, PropertyChangeMask must be
+ * selected on all root windows for a display. liblf does not do this
+ * for you because it's pretty likely to mess something up. So you
+ * have to do it yourself in programs that use #LfMonitorContext.
+ *
+ * Return value: a new #LfMonitorContext
+ **/
+LfMonitorContext*
+lf_monitor_context_new (LfDisplay *display,
+ LfMonitorEventFunc event_func,
+ void *event_func_data,
+ LfFreeFunc free_data_func)
+{
+ LfMonitorContext *context;
+
+ context = lf_new0 (LfMonitorContext, 1);
+
+ context->refcount = 1;
+ context->event_func = event_func;
+ context->event_func_data = event_func_data;
+ context->free_data_func = free_data_func;
+
+ context->display = display;
+ lf_display_ref (context->display);
+
+ if (context_list == NULL)
+ context_list = lf_list_new ();
+
+ if (lf_list_empty (context_list))
+ lf_internal_add_xmessage_func (display,
+ KDE_STARTUP_INFO_ATOM,
+ xmessage_func,
+ NULL, NULL);
+
+ lf_list_prepend (context_list, context);
+
+ /* We get events for serials >= creation_serial */
+ context->creation_serial = next_sequence_serial;
+
+ return context;
+}
+
+/**
+ * lf_monitor_context_ref:
+ * @context: an #LfMonitorContext
+ *
+ * Increments the reference count on @context.
+ *
+ **/
+void
+lf_monitor_context_ref (LfMonitorContext *context)
+{
+ context->refcount += 1;
+}
+
+/**
+ * lf_monitor_context_unref:
+ * @context: an #LfMonitorContext
+ *
+ * Decrements the reference count on @context and frees the
+ * context if the count reaches 0.
+ **/
+void
+lf_monitor_context_unref (LfMonitorContext *context)
+{
+ context->refcount -= 1;
+
+ if (context->refcount == 0)
+ {
+ lf_list_remove (context_list, context);
+
+ if (lf_list_empty (context_list))
+ lf_internal_remove_xmessage_func (context->display,
+ KDE_STARTUP_INFO_ATOM,
+ xmessage_func,
+ NULL);
+
+ if (context->free_data_func)
+ (* context->free_data_func) (context->event_func_data);
+
+ lf_display_unref (context->display);
+ lf_free (context);
+ }
+}
+
+void
+lf_monitor_event_ref (LfMonitorEvent *event)
+{
+ event->refcount += 1;
+}
+
+void
+lf_monitor_event_unref (LfMonitorEvent *event)
+{
+ event->refcount -= 1;
+
+ if (event->refcount == 0)
+ {
+ if (event->context)
+ lf_monitor_context_unref (event->context);
+ if (event->sequence)
+ lf_launch_sequence_unref (event->sequence);
+ lf_free (event);
+ }
+}
+
+LfMonitorEvent*
+lf_monitor_event_copy (LfMonitorEvent *event)
+{
+ LfMonitorEvent *copy;
+
+ copy = lf_new0 (LfMonitorEvent, 1);
+
+ copy->refcount = 1;
+
+ copy->type = event->type;
+ copy->context = event->context;
+ if (copy->context)
+ lf_monitor_context_ref (copy->context);
+ copy->sequence = event->sequence;
+ if (copy->sequence)
+ lf_launch_sequence_ref (copy->sequence);
+
+ copy->timestamp = event->timestamp;
+
+ return copy;
+}
+
+LfMonitorEventType
+lf_monitor_event_get_type (LfMonitorEvent *event)
+{
+ return event->type;
+}
+
+LfLaunchSequence*
+lf_monitor_event_get_launch_sequence (LfMonitorEvent *event)
+{
+ return event->sequence;
+}
+
+LfMonitorContext*
+lf_monitor_event_get_context (LfMonitorEvent *event)
+{
+ return event->context;
+}
+
+Time
+lf_monitor_event_get_time (LfMonitorEvent *event)
+{
+ return event->timestamp;
+}
+
+static void
+update_geometry (LfLaunchSequence *sequence)
+{
+ int *vals;
+ int n_vals;
+
+ sequence->geometry_set = FALSE;
+
+ vals = NULL;
+ n_vals = 0;
+ if (lf_internal_get_cardinal_list (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_GEOMETRY",
+ &vals, &n_vals) &&
+ n_vals == 4)
+ {
+ sequence->x = vals[0];
+ sequence->y = vals[1];
+ sequence->width = vals[2];
+ sequence->height = vals[3];
+ }
+
+ lf_free (vals);
+}
+
+static void
+update_pid (LfLaunchSequence *sequence)
+{
+ int val;
+
+ sequence->pid = -1;
+
+ val = -1;
+ if (lf_internal_get_cardinal (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_PID",
+ &val))
+ sequence->pid = val;
+}
+
+static LfLaunchSequence*
+lf_launch_sequence_new (LfDisplay *display,
+ Window launch_window)
+{
+ LfLaunchSequence *sequence;
+ char *id;
+ int val;
+
+ /* Select input, then get _NET_LAUNCH_ID,
+ * because we want to be sure we detect a BadWindow
+ * if it happens prior to selecting input, and
+ * getting _NET_LAUNCH_ID will bomb on BadWindow
+ */
+ if (launch_window != None) /* launch_window may be NULL for xmessage sequence */
+ {
+ lf_display_error_trap_push (display);
+ XSelectInput (lf_display_get_x_display (display),
+ launch_window,
+ PropertyChangeMask | StructureNotifyMask);
+ lf_display_error_trap_pop (display);
+
+ id = NULL;
+ if (!lf_internal_get_string (display, launch_window,
+ "_NET_LAUNCH_ID", &id))
+ {
+ return NULL;
+ }
+ }
+
+ sequence = lf_new0 (LfLaunchSequence, 1);
+
+ sequence->refcount = 1;
+
+ sequence->creation_serial = next_sequence_serial;
+ ++next_sequence_serial;
+
+ sequence->id = id;
+ sequence->launch_window = launch_window;
+ sequence->display = display;
+ lf_display_ref (display);
+
+ sequence->workspace = -1; /* not set */
+ sequence->pid = -1;
+
+ /* Update stuff that can change over time */
+ update_geometry (sequence);
+ update_pid (sequence);
+
+ if (sequence->launch_window != None)
+ {
+ /* Grab all the stuff that can't be changed later */
+ lf_internal_get_utf8_string (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_NAME",
+ &sequence->name);
+ lf_internal_get_utf8_string (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_DESCRIPTION",
+ &sequence->description);
+ lf_internal_get_string (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_LEGACY_RESOURCE_CLASS",
+ &sequence->resource_class);
+ lf_internal_get_string (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_LEGACY_RESOURCE_NAME",
+ &sequence->resource_name);
+ lf_internal_get_string (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_LEGACY_NAME",
+ &sequence->window_title);
+ if (lf_internal_get_cardinal (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_DESKTOP",
+ &val))
+ sequence->workspace = val;
+ lf_internal_get_string (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_BINARY_NAME",
+ &sequence->binary_name);
+ lf_internal_get_string (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_HOSTNAME",
+ &sequence->hostname);
+ lf_internal_get_string (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_ICON_NAME",
+ &sequence->icon_name);
+ lf_internal_get_window (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_GEOMETRY_WINDOW",
+ &sequence->geometry_window);
+
+ if (lf_internal_get_cardinal (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_SUPPORTS_CANCEL",
+ &val))
+ sequence->supports_cancel = val != 0;
+ }
+
+ return sequence;
+}
+
+void
+lf_launch_sequence_ref (LfLaunchSequence *sequence)
+{
+ sequence->refcount += 1;
+}
+
+void
+lf_launch_sequence_unref (LfLaunchSequence *sequence)
+{
+ sequence->refcount -= 1;
+
+ if (sequence->refcount == 0)
+ {
+ lf_free (sequence->id);
+
+ lf_free (sequence->name);
+ lf_free (sequence->description);
+ lf_free (sequence->resource_class);
+ lf_free (sequence->resource_name);
+ lf_free (sequence->window_title);
+ lf_free (sequence->binary_name);
+ lf_free (sequence->icon_name);
+ lf_free (sequence->hostname);
+
+ lf_display_unref (sequence->display);
+ lf_free (sequence);
+ }
+}
+
+const char*
+lf_launch_sequence_get_id (LfLaunchSequence *sequence)
+{
+ return sequence->id;
+}
+
+Window
+lf_launch_sequence_get_window (LfLaunchSequence *sequence)
+{
+ return sequence->launch_window;
+}
+
+lf_bool_t
+lf_launch_sequence_get_geometry (LfLaunchSequence *sequence,
+ int *x,
+ int *y,
+ int *width,
+ int *height)
+{
+ if (sequence->geometry_set)
+ {
+ *x = sequence->x;
+ *y = sequence->y;
+ *width = sequence->width;
+ *height = sequence->height;
+ return TRUE;
+ }
+ else
+ {
+ *x = 0;
+ *y = 0;
+ *width = 0;
+ *height = 0;
+ return FALSE;
+ }
+}
+
+Window
+lf_launch_sequence_get_geometry_window (LfLaunchSequence *sequence)
+{
+ return sequence->geometry_window;
+}
+
+lf_bool_t
+lf_launch_sequence_get_completed (LfLaunchSequence *sequence)
+{
+ return sequence->completed;
+}
+
+lf_bool_t
+lf_launch_sequence_get_canceled (LfLaunchSequence *sequence)
+{
+ return sequence->canceled;
+}
+
+const char*
+lf_launch_sequence_get_name (LfLaunchSequence *sequence)
+{
+ return sequence->name;
+}
+
+const char*
+lf_launch_sequence_get_description (LfLaunchSequence *sequence)
+{
+ return sequence->description;
+}
+
+int
+lf_launch_sequence_get_workspace (LfLaunchSequence *sequence)
+{
+ return sequence->workspace;
+}
+
+const char*
+lf_launch_sequence_get_legacy_resource_class (LfLaunchSequence *sequence)
+{
+ return sequence->resource_class;
+}
+
+const char*
+lf_launch_sequence_get_legacy_resource_name (LfLaunchSequence *sequence)
+{
+ return sequence->resource_name;
+}
+
+const char*
+lf_launch_sequence_get_legacy_window_title (LfLaunchSequence *sequence)
+{
+ return sequence->window_title;
+}
+
+lf_bool_t
+lf_launch_sequence_get_supports_cancel (LfLaunchSequence *sequence)
+{
+ return sequence->supports_cancel;
+}
+
+int
+lf_launch_sequence_get_pid (LfLaunchSequence *sequence)
+{
+ return sequence->pid;
+}
+
+const char*
+lf_launch_sequence_get_binary_name (LfLaunchSequence *sequence)
+{
+ return sequence->binary_name;
+}
+
+const char*
+lf_launch_sequence_get_hostname (LfLaunchSequence *sequence)
+{
+ return sequence->hostname;
+}
+
+const char*
+lf_launch_sequence_get_icon_name (LfLaunchSequence *sequence)
+{
+ return sequence->icon_name;
+}
+
+void
+lf_launch_sequence_cancel (LfLaunchSequence *sequence)
+{
+ if (sequence->supports_cancel)
+ lf_internal_set_cardinal (sequence->display,
+ sequence->launch_window,
+ "_NET_LAUNCH_CANCELED",
+ 0);
+}
+
+static lf_bool_t
+check_cardinal_exists (LfDisplay *display,
+ Window xwindow,
+ const char *property)
+{
+ int val;
+
+ return lf_internal_get_cardinal (display, xwindow, property,
+ &val);
+}
+
+typedef struct
+{
+ LfMonitorEvent *base_event;
+ LfList *events;
+} CreateContextEventsData;
+
+static lf_bool_t
+create_context_events_foreach (void *value,
+ void *data)
+{
+ /* Make a list of events holding a ref to the context they'll go to,
+ * for reentrancy robustness
+ */
+ LfMonitorContext *context = value;
+ CreateContextEventsData *ced = data;
+
+ /* Don't send events for launch sequences initiated before the
+ * context was created
+ */
+ if (ced->base_event->sequence->creation_serial >=
+ context->creation_serial)
+ {
+ LfMonitorEvent *copy;
+
+ copy = lf_monitor_event_copy (ced->base_event);
+ copy->context = context;
+ lf_monitor_context_ref (copy->context);
+
+ lf_list_prepend (ced->events, copy);
+ }
+
+ return TRUE;
+}
+
+static lf_bool_t
+dispatch_event_foreach (void *value,
+ void *data)
+{
+ LfMonitorEvent *event = value;
+
+ /* Dispatch and free events */
+
+ if (event->context->event_func)
+ (* event->context->event_func) (event,
+ event->context->event_func_data);
+
+ lf_monitor_event_unref (event);
+
+ return TRUE;
+}
+
+static lf_bool_t
+filter_event (LfMonitorEvent *event)
+{
+ lf_bool_t retval;
+
+ retval = FALSE;
+
+ /* Filter out duplicate events and update flags */
+ switch (event->type)
+ {
+ case LF_MONITOR_EVENT_CANCELED:
+ if (event->sequence->canceled)
+ {
+ retval = TRUE;
+ }
+ else
+ {
+ event->sequence->canceled = TRUE;
+ }
+ break;
+ case LF_MONITOR_EVENT_COMPLETED:
+ if (event->sequence->completed)
+ {
+ retval = TRUE;
+ }
+ else
+ {
+ event->sequence->completed = TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+typedef struct
+{
+ LfDisplay *display;
+ Window launch_window;
+ LfLaunchSequence *found;
+} FindSequenceData;
+
+static lf_bool_t
+find_sequence_foreach (void *value,
+ void *data)
+{
+ LfLaunchSequence *sequence = value;
+ FindSequenceData *fsd = data;
+
+ if (sequence->launch_window == fsd->launch_window &&
+ lf_display_get_x_display (sequence->display) ==
+ lf_display_get_x_display (fsd->display))
+ {
+ fsd->found = sequence;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static LfLaunchSequence*
+find_sequence_for_window (LfDisplay *display,
+ Window event_window)
+{
+ FindSequenceData fsd;
+
+ if (sequence_list == NULL)
+ return NULL;
+
+ fsd.display = display;
+ fsd.launch_window = event_window;
+ fsd.found = NULL;
+
+ lf_list_foreach (sequence_list, find_sequence_foreach, &fsd);
+
+ return fsd.found;
+}
+
+static LfLaunchSequence*
+add_sequence (LfDisplay *display,
+ Window event_xwindow)
+{
+ LfLaunchSequence *sequence;
+
+ sequence =
+ lf_launch_sequence_new (display, event_xwindow);
+
+ if (sequence)
+ {
+ lf_launch_sequence_ref (sequence); /* ref held by sequence list */
+ if (sequence_list == NULL)
+ sequence_list = lf_list_new ();
+ lf_list_prepend (sequence_list, sequence);
+ }
+
+ return sequence;
+}
+
+static void
+remove_sequence (LfLaunchSequence *sequence)
+{
+ lf_list_remove (sequence_list, sequence);
+ lf_launch_sequence_unref (sequence);
+}
+
+static void
+dispatch_monitor_event (LfDisplay *display,
+ LfMonitorEvent *event,
+ Window event_xwindow)
+{
+ if (event->type == LF_MONITOR_EVENT_INITIATED)
+ {
+ if (event->sequence == NULL)
+ event->sequence = add_sequence (display, event_xwindow);
+ }
+ else if (event->sequence == NULL)
+ {
+ event->sequence = find_sequence_for_window (display,
+ event_xwindow);
+ if (event->sequence)
+ lf_launch_sequence_ref (event->sequence); /* ref held by event */
+ }
+
+ if (event->sequence != NULL)
+ {
+ switch (event->type)
+ {
+ case LF_MONITOR_EVENT_GEOMETRY_CHANGED:
+ update_geometry (event->sequence);
+ break;
+
+ case LF_MONITOR_EVENT_PID_CHANGED:
+ update_pid (event->sequence);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (event->sequence != NULL &&
+ !filter_event (event))
+ {
+ CreateContextEventsData cced;
+
+ cced.base_event = event;
+ cced.events = lf_list_new ();
+
+ lf_list_foreach (context_list, create_context_events_foreach,
+ &cced);
+
+ lf_list_foreach (cced.events, dispatch_event_foreach, NULL);
+
+ /* values in the events list freed on dispatch */
+ lf_list_free (cced.events);
+
+ /* remove from sequence list */
+ if (event->type == LF_MONITOR_EVENT_COMPLETED)
+ remove_sequence (event->sequence);
+ }
+}
+
+lf_bool_t
+lf_internal_monitor_process_event (LfDisplay *display,
+ XEvent *xevent)
+{
+ lf_bool_t retval;
+ LfMonitorEvent *event;
+ Window event_xwindow;
+
+ if (context_list == NULL ||
+ lf_list_empty (context_list))
+ return FALSE; /* no one cares */
+
+ event_xwindow = None;
+ event = NULL;
+ retval = FALSE;
+
+ switch (xevent->xany.type)
+ {
+ case PropertyNotify:
+ if (xevent->xproperty.atom ==
+ lf_internal_atom_get (display, "_NET_LAUNCH_CANCELED"))
+ {
+ event_xwindow = xevent->xproperty.window;
+
+ if (check_cardinal_exists (display, event_xwindow,
+ "_NET_LAUNCH_CANCELED"))
+ {
+ event = lf_new (LfMonitorEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_MONITOR_EVENT_CANCELED;
+ event->timestamp = xevent->xproperty.time;
+ event->context = NULL;
+ event->sequence = NULL;
+
+ retval = TRUE;
+ }
+ }
+ else if (xevent->xproperty.atom ==
+ lf_internal_atom_get (display, "_NET_LAUNCH_COMPLETE"))
+ {
+ event_xwindow = xevent->xproperty.window;
+
+ if (check_cardinal_exists (display, event_xwindow,
+ "_NET_LAUNCH_COMPLETE"))
+ {
+ event = lf_new (LfMonitorEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_MONITOR_EVENT_COMPLETED;
+ event->timestamp = xevent->xproperty.time;
+ event->context = NULL;
+ event->sequence = NULL;
+
+ retval = TRUE;
+ }
+ }
+ else if (xevent->xproperty.atom ==
+ lf_internal_atom_get (display, "_NET_LAUNCH_GEOMETRY"))
+ {
+ event_xwindow = xevent->xproperty.window;
+
+ event = lf_new (LfMonitorEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_MONITOR_EVENT_GEOMETRY_CHANGED;
+ event->timestamp = xevent->xproperty.time;
+ event->context = NULL;
+ event->sequence = NULL;
+
+ retval = TRUE;
+ }
+ else if (xevent->xproperty.atom ==
+ lf_internal_atom_get (display, "_NET_LAUNCH_PID"))
+ {
+ event_xwindow = xevent->xproperty.window;
+
+ event = lf_new (LfMonitorEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_MONITOR_EVENT_PID_CHANGED;
+ event->timestamp = xevent->xproperty.time;
+ event->context = NULL;
+ event->sequence = NULL;
+
+ retval = TRUE;
+ }
+ break;
+
+ case ClientMessage:
+ if (xevent->xclient.message_type ==
+ lf_internal_atom_get (display,
+ "_NET_LAUNCH_PULSE"))
+ {
+ event_xwindow = xevent->xclient.window;
+
+ event = lf_new (LfMonitorEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_MONITOR_EVENT_PULSE;
+ event->timestamp = CurrentTime;
+ event->context = NULL;
+ event->sequence = NULL;
+
+ retval = TRUE;
+ }
+ else if (xevent->xclient.message_type ==
+ lf_internal_atom_get (display,
+ "_NET_LAUNCH_INITIATE"))
+ {
+ LfLaunchSequence *sequence;
+
+ /* Don't be fooled by duplicate initiate messages -
+ * check that the sequence doesn't exist yet
+ */
+ sequence = find_sequence_for_window (display,
+ xevent->xclient.window);
+
+ if (sequence == NULL)
+ {
+ event_xwindow = xevent->xclient.window;
+
+ event = lf_new (LfMonitorEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_MONITOR_EVENT_INITIATED;
+ event->timestamp = xevent->xclient.data.l[0];
+ event->context = NULL;
+ event->sequence = NULL;
+ }
+
+ retval = TRUE;
+ }
+ break;
+
+ case DestroyNotify:
+ {
+ LfLaunchSequence *sequence;
+
+ sequence = find_sequence_for_window (display,
+ xevent->xdestroywindow.window);
+
+ if (sequence != NULL)
+ {
+ event_xwindow = xevent->xdestroywindow.window;
+
+ event = lf_new (LfMonitorEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_MONITOR_EVENT_COMPLETED;
+ event->timestamp = CurrentTime;
+ event->context = NULL;
+ event->sequence = sequence;
+ lf_launch_sequence_ref (sequence);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (event != NULL)
+ {
+ dispatch_monitor_event (display, event, event_xwindow);
+
+ lf_monitor_event_unref (event);
+ }
+
+ return retval;
+}
+
+typedef struct
+{
+ LfDisplay *display;
+ const char *id;
+ LfLaunchSequence *found;
+} FindSequenceByIdData;
+
+static lf_bool_t
+find_sequence_by_id_foreach (void *value,
+ void *data)
+{
+ LfLaunchSequence *sequence = value;
+ FindSequenceByIdData *fsd = data;
+
+ if (strcmp (sequence->id, fsd->id) == 0 &&
+ lf_display_get_x_display (sequence->display) ==
+ lf_display_get_x_display (fsd->display))
+ {
+ fsd->found = sequence;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static LfLaunchSequence*
+find_sequence_for_id (LfDisplay *display,
+ const char *id)
+{
+ FindSequenceByIdData fsd;
+
+ if (sequence_list == NULL)
+ return NULL;
+
+ fsd.display = display;
+ fsd.id = id;
+ fsd.found = NULL;
+
+ lf_list_foreach (sequence_list, find_sequence_by_id_foreach, &fsd);
+
+ return fsd.found;
+}
+
+static lf_bool_t
+do_xmessage_event_foreach (void *value,
+ void *data)
+{
+ LfMonitorEvent *event = value;
+ LfDisplay *display = data;
+
+ dispatch_monitor_event (display, event, None);
+
+ return TRUE;
+}
+
+static lf_bool_t
+unref_event_foreach (void *value,
+ void *data)
+{
+ lf_monitor_event_unref (value);
+ return TRUE;
+}
+
+static void
+xmessage_func (LfDisplay *display,
+ const char *message_type,
+ const char *message,
+ void *user_data)
+{
+ /* assert (strcmp (message_type, KDE_STARTUP_INFO_ATOM) == 0); */
+ char *prefix;
+ char **names;
+ char **values;
+ int i;
+ const char *launch_id;
+ LfLaunchSequence *sequence;
+ LfList *events;
+
+ prefix = NULL;
+ names = NULL;
+ values = NULL;
+ if (!lf_internal_unserialize_message (message, &prefix, &names, &values))
+ return;
+
+ launch_id = NULL;
+ i = 0;
+ while (names[i])
+ {
+ if (strcmp (names[i], "ID") == 0)
+ {
+ launch_id = values[i];
+ break;
+ }
+ ++i;
+ }
+
+ events = lf_list_new ();
+
+ if (launch_id == NULL)
+ goto out;
+
+ sequence = find_sequence_for_id (display, launch_id);
+
+ if (strcmp (prefix, "new") == 0)
+ {
+ if (sequence == NULL)
+ {
+ LfMonitorEvent *event;
+
+ sequence = add_sequence (display, None);
+ if (sequence == NULL)
+ goto out;
+
+ sequence->id = lf_internal_strdup (launch_id);
+
+ event = lf_new (LfMonitorEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_MONITOR_EVENT_INITIATED;
+ event->timestamp = CurrentTime;
+ event->context = NULL;
+ event->sequence = sequence; /* ref from add_sequence goes here */
+
+ lf_list_append (events, event);
+ }
+ }
+
+ if (sequence == NULL)
+ goto out;
+
+ if (strcmp (prefix, "change") == 0 ||
+ strcmp (prefix, "new") == 0)
+ {
+ lf_bool_t random_stuff_changed = FALSE;
+ lf_bool_t pid_changed = FALSE;
+ lf_bool_t workspace_changed = FALSE;
+
+ i = 0;
+ while (names[i])
+ {
+ if (strcmp (names[i], "BIN") == 0)
+ {
+ if (sequence->binary_name == NULL)
+ {
+ sequence->binary_name = lf_internal_strdup (values[i]);
+ random_stuff_changed = TRUE;
+ }
+ }
+ else if (strcmp (names[i], "NAME") == 0)
+ {
+ if (sequence->name == NULL)
+ {
+ sequence->name = lf_internal_strdup (values[i]);
+ random_stuff_changed = TRUE;
+ }
+ }
+ else if (strcmp (names[i], "ICON") == 0)
+ {
+ if (sequence->icon_name == NULL)
+ {
+ sequence->icon_name = lf_internal_strdup (values[i]);
+ random_stuff_changed = TRUE;
+ }
+ }
+ else if (strcmp (names[i], "DESKTOP") == 0)
+ {
+ int workspace;
+
+ workspace = lf_internal_string_to_ulong (values[i]);
+
+ sequence->workspace = workspace;
+ workspace_changed = TRUE;
+ }
+ else if (strcmp (names[i], "WMCLASS") == 0)
+ {
+ if (sequence->resource_class == NULL)
+ {
+ sequence->resource_class = lf_internal_strdup (values[i]);
+ random_stuff_changed = TRUE;
+ }
+ }
+ else if (strcmp (names[i], "PID") == 0)
+ {
+ int pid;
+
+ pid = lf_internal_string_to_ulong (values[i]);
+
+ if (pid > 0)
+ {
+ sequence->pid = pid;
+
+ pid_changed = TRUE;
+ }
+ }
+ else if (strcmp (names[i], "HOSTNAME") == 0)
+ {
+ if (sequence->hostname == NULL)
+ {
+ sequence->hostname = lf_internal_strdup (values[i]);
+ random_stuff_changed = TRUE;
+ }
+ }
+
+ ++i;
+ }
+
+ if (pid_changed)
+ {
+ LfMonitorEvent *event;
+
+ event = lf_new (LfMonitorEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_MONITOR_EVENT_PID_CHANGED;
+ event->timestamp = CurrentTime;
+ event->context = NULL;
+ event->sequence = sequence;
+ lf_launch_sequence_ref (sequence);
+
+ lf_list_append (events, event);
+ }
+
+ if (workspace_changed)
+ {
+ LfMonitorEvent *event;
+
+ event = lf_new (LfMonitorEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_MONITOR_EVENT_WORKSPACE_CHANGED;
+ event->timestamp = CurrentTime;
+ event->context = NULL;
+ event->sequence = sequence;
+ lf_launch_sequence_ref (sequence);
+
+ lf_list_append (events, event);
+ }
+
+ if (random_stuff_changed)
+ {
+ /* FIXME */
+ }
+ }
+ else if (strcmp (prefix, "remove") == 0)
+ {
+ LfMonitorEvent *event;
+
+ event = lf_new (LfMonitorEvent, 1);
+
+ event->refcount = 1;
+ event->type = LF_MONITOR_EVENT_COMPLETED;
+ event->timestamp = CurrentTime;
+ event->context = NULL;
+ event->sequence = sequence;
+ lf_launch_sequence_ref (sequence);
+
+ lf_list_append (events, event);
+ }
+
+ lf_list_foreach (events,
+ do_xmessage_event_foreach,
+ display);
+
+ out:
+ if (events != NULL)
+ {
+ lf_list_foreach (events, unref_event_foreach, NULL);
+ lf_list_free (events);
+ }
+
+ lf_free (prefix);
+ lf_internal_strfreev (names);
+ lf_internal_strfreev (values);
+}
diff --git a/libsn/sn-monitor.h b/libsn/sn-monitor.h
new file mode 100644
index 0000000..e9efdd9
--- /dev/null
+++ b/libsn/sn-monitor.h
@@ -0,0 +1,97 @@
+/* Monitor API - if you are a program that monitors launch sequences */
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+#ifndef __LF_MONITOR_H__
+#define __LF_MONITOR_H__
+
+#include <liblf/lf-common.h>
+
+LF_BEGIN_DECLS
+
+typedef struct LfMonitorContext LfMonitorContext;
+typedef struct LfMonitorEvent LfMonitorEvent;
+typedef struct LfLaunchSequence LfLaunchSequence;
+
+typedef void (* LfMonitorEventFunc) (LfMonitorEvent *event,
+ void *user_data);
+
+typedef enum
+{
+ LF_MONITOR_EVENT_INITIATED,
+ LF_MONITOR_EVENT_COMPLETED,
+ LF_MONITOR_EVENT_CANCELED,
+ LF_MONITOR_EVENT_PULSE,
+ LF_MONITOR_EVENT_GEOMETRY_CHANGED,
+ LF_MONITOR_EVENT_PID_CHANGED,
+ /* only allowed with xmessages protocol */
+ LF_MONITOR_EVENT_WORKSPACE_CHANGED
+} LfMonitorEventType;
+
+LfMonitorContext* lf_monitor_context_new (LfDisplay *display,
+ LfMonitorEventFunc event_func,
+ void *event_func_data,
+ LfFreeFunc free_data_func);
+void lf_monitor_context_ref (LfMonitorContext *context);
+void lf_monitor_context_unref (LfMonitorContext *context);
+
+
+void lf_monitor_event_ref (LfMonitorEvent *event);
+void lf_monitor_event_unref (LfMonitorEvent *event);
+LfMonitorEvent* lf_monitor_event_copy (LfMonitorEvent *event);
+LfMonitorEventType lf_monitor_event_get_type (LfMonitorEvent *event);
+LfLaunchSequence* lf_monitor_event_get_launch_sequence (LfMonitorEvent *event);
+LfMonitorContext* lf_monitor_event_get_context (LfMonitorEvent *event);
+Time lf_monitor_event_get_time (LfMonitorEvent *event);
+
+void lf_launch_sequence_ref (LfLaunchSequence *sequence);
+void lf_launch_sequence_unref (LfLaunchSequence *sequence);
+const char* lf_launch_sequence_get_id (LfLaunchSequence *sequence);
+Window lf_launch_sequence_get_window (LfLaunchSequence *sequence);
+lf_bool_t lf_launch_sequence_get_geometry (LfLaunchSequence *sequence,
+ int *x,
+ int *y,
+ int *width,
+ int *height);
+Window lf_launch_sequence_get_geometry_window (LfLaunchSequence *sequence);
+lf_bool_t lf_launch_sequence_get_completed (LfLaunchSequence *sequence);
+lf_bool_t lf_launch_sequence_get_canceled (LfLaunchSequence *sequence);
+const char* lf_launch_sequence_get_name (LfLaunchSequence *sequence);
+const char* lf_launch_sequence_get_description (LfLaunchSequence *sequence);
+int lf_launch_sequence_get_workspace (LfLaunchSequence *sequence);
+const char* lf_launch_sequence_get_legacy_resource_class (LfLaunchSequence *sequence);
+const char* lf_launch_sequence_get_legacy_resource_name (LfLaunchSequence *sequence);
+const char* lf_launch_sequence_get_legacy_window_title (LfLaunchSequence *sequence);
+lf_bool_t lf_launch_sequence_get_supports_cancel (LfLaunchSequence *sequence);
+int lf_launch_sequence_get_pid (LfLaunchSequence *sequence);
+const char* lf_launch_sequence_get_binary_name (LfLaunchSequence *sequence);
+const char* lf_launch_sequence_get_hostname (LfLaunchSequence *sequence);
+const char* lf_launch_sequence_get_icon_name (LfLaunchSequence *sequence);
+
+void lf_launch_sequence_cancel (LfLaunchSequence *sequence);
+
+LF_END_DECLS
+
+#endif /* __LF_MONITOR_H__ */
diff --git a/libsn/sn-util.c b/libsn/sn-util.c
new file mode 100644
index 0000000..7a07b26
--- /dev/null
+++ b/libsn/sn-util.c
@@ -0,0 +1,310 @@
+/* This code derived from GLib, Copyright (C) 1997-2002
+ * by the GLib team.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include "lf-util.h"
+#include "lf-internals.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#ifndef REALLOC_0_WORKS
+static void*
+standard_realloc (void* mem,
+ lf_size_t n_bytes)
+{
+ if (!mem)
+ return malloc (n_bytes);
+ else
+ return realloc (mem, n_bytes);
+}
+#endif /* !REALLOC_0_WORKS */
+
+#ifdef SANE_MALLOC_PROTOS
+# define standard_malloc malloc
+# ifdef REALLOC_0_WORKS
+# define standard_realloc realloc
+# endif /* REALLOC_0_WORKS */
+# define standard_free free
+# define standard_calloc calloc
+# define standard_try_malloc malloc
+# define standard_try_realloc realloc
+#else /* !SANE_MALLOC_PROTOS */
+static void*
+standard_malloc (lf_size_t n_bytes)
+{
+ return malloc (n_bytes);
+}
+# ifdef REALLOC_0_WORKS
+static void*
+standard_realloc (void* mem,
+ lf_size_t n_bytes)
+{
+ return realloc (mem, n_bytes);
+}
+# endif /* REALLOC_0_WORKS */
+static void
+standard_free (void* mem)
+{
+ free (mem);
+}
+static void*
+standard_calloc (lf_size_t n_blocks,
+ lf_size_t n_bytes)
+{
+ return calloc (n_blocks, n_bytes);
+}
+#define standard_try_malloc standard_malloc
+#define standard_try_realloc standard_realloc
+#endif /* !SANE_MALLOC_PROTOS */
+
+
+/* --- variables --- */
+static LfMemVTable lf_mem_vtable = {
+ standard_malloc,
+ standard_realloc,
+ standard_free,
+ standard_calloc,
+ standard_try_malloc,
+ standard_try_realloc,
+ NULL,
+ NULL
+};
+
+
+/* --- functions --- */
+void*
+lf_malloc (lf_size_t n_bytes)
+{
+ if (n_bytes)
+ {
+ void* mem;
+
+ mem = lf_mem_vtable.malloc (n_bytes);
+ if (mem)
+ return mem;
+
+ fprintf (stderr,
+ "liblf: failed to allocate %lu bytes",
+ (unsigned long) n_bytes);
+ }
+
+ return NULL;
+}
+
+void*
+lf_malloc0 (lf_size_t n_bytes)
+{
+ if (n_bytes)
+ {
+ void* mem;
+
+ mem = lf_mem_vtable.calloc (1, n_bytes);
+ if (mem)
+ return mem;
+
+ fprintf (stderr,
+ "liblf: failed to allocate %lu bytes",
+ (unsigned long) n_bytes);
+ }
+
+ return NULL;
+}
+
+void*
+lf_realloc (void *mem,
+ lf_size_t n_bytes)
+{
+ if (n_bytes)
+ {
+ mem = lf_mem_vtable.realloc (mem, n_bytes);
+ if (mem)
+ return mem;
+
+ fprintf (stderr,
+ "liblf: failed to allocate %lu bytes",
+ (unsigned long) n_bytes);
+ }
+
+ if (mem)
+ lf_mem_vtable.free (mem);
+
+ return NULL;
+}
+
+void
+lf_free (void* mem)
+{
+ if (mem)
+ lf_mem_vtable.free (mem);
+}
+
+void*
+lf_try_malloc (lf_size_t n_bytes)
+{
+ if (n_bytes)
+ return lf_mem_vtable.try_malloc (n_bytes);
+ else
+ return NULL;
+}
+
+void*
+lf_try_realloc (void *mem,
+ lf_size_t n_bytes)
+{
+ if (n_bytes)
+ return lf_mem_vtable.try_realloc (mem, n_bytes);
+
+ if (mem)
+ lf_mem_vtable.free (mem);
+
+ return NULL;
+}
+
+static void*
+fallback_calloc (lf_size_t n_blocks,
+ lf_size_t n_block_bytes)
+{
+ lf_size_t l = n_blocks * n_block_bytes;
+ void* mem = lf_mem_vtable.malloc (l);
+
+ if (mem)
+ memset (mem, 0, l);
+
+ return mem;
+}
+
+static lf_bool_t vtable_set = FALSE;
+
+lf_bool_t
+lf_mem_is_system_malloc (void)
+{
+ return !vtable_set;
+}
+
+void
+lf_mem_set_vtable (LfMemVTable *vtable)
+{
+ if (!vtable_set)
+ {
+ vtable_set = TRUE;
+ if (vtable->malloc && vtable->realloc && vtable->free)
+ {
+ lf_mem_vtable.malloc = vtable->malloc;
+ lf_mem_vtable.realloc = vtable->realloc;
+ lf_mem_vtable.free = vtable->free;
+ lf_mem_vtable.calloc = vtable->calloc ? vtable->calloc : fallback_calloc;
+ lf_mem_vtable.try_malloc = vtable->try_malloc ? vtable->try_malloc : lf_mem_vtable.malloc;
+ lf_mem_vtable.try_realloc = vtable->try_realloc ? vtable->try_realloc : lf_mem_vtable.realloc;
+ }
+ else
+ {
+ fprintf (stderr,
+ "liblf: memory allocation vtable lacks one of malloc(), realloc() or free()");
+ }
+ }
+ else
+ {
+ fprintf (stderr,
+ "liblf: memory allocation vtable can only be set once at startup");
+ }
+}
+
+static LfUtf8ValidateFunc utf8_validator = NULL;
+
+void
+lf_set_utf8_validator (LfUtf8ValidateFunc validate_func)
+{
+ utf8_validator = validate_func;
+}
+
+lf_bool_t
+lf_internal_utf8_validate (const char *str,
+ int max_len)
+{
+ if (utf8_validator)
+ {
+ if (max_len < 0)
+ max_len = strlen (str);
+ return (* utf8_validator) (str, max_len);
+ }
+ else
+ return TRUE;
+}
+
+char*
+lf_internal_strdup (const char *str)
+{
+ char *s;
+
+ s = lf_malloc (strlen (str) + 1);
+ strcpy (s, str);
+
+ return s;
+}
+
+char*
+lf_internal_strndup (const char *str,
+ int n)
+{
+ char *new_str;
+
+ if (str)
+ {
+ new_str = lf_new (char, n + 1);
+ strncpy (new_str, str, n);
+ new_str[n] = '\0';
+ }
+ else
+ new_str = NULL;
+
+ return new_str;
+}
+
+void
+lf_internal_strfreev (char **strings)
+{
+ int i;
+
+ if (strings == NULL)
+ return;
+
+ i = 0;
+ while (strings[i])
+ {
+ lf_free (strings[i]);
+ ++i;
+ }
+ lf_free (strings);
+}
+
+unsigned long
+lf_internal_string_to_ulong (const char* str)
+{
+ unsigned long retval;
+ char *end;
+ errno = 0;
+ retval = strtoul (str, &end, 0);
+ if (end == str || errno != 0)
+ retval = 0;
+
+ return retval;
+}
diff --git a/libsn/sn-util.h b/libsn/sn-util.h
new file mode 100644
index 0000000..4da3c1e
--- /dev/null
+++ b/libsn/sn-util.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+#ifndef __LF_UTIL_H__
+#define __LF_UTIL_H__
+
+/* Guard C code in headers, while including them from C++ */
+#ifdef __cplusplus
+# define LF_BEGIN_DECLS extern "C" {
+# define LF_END_DECLS }
+#else
+# define LF_BEGIN_DECLS
+# define LF_END_DECLS
+#endif
+
+LF_BEGIN_DECLS
+
+typedef unsigned long lf_size_t;
+typedef int lf_bool_t;
+
+/* Padding in vtables */
+typedef void (* LfPaddingFunc) (void);
+/* Free data */
+typedef void (* LfFreeFunc) (void *data);
+
+void* lf_malloc (lf_size_t n_bytes);
+void* lf_malloc0 (lf_size_t n_bytes);
+void* lf_realloc (void *mem,
+ lf_size_t n_bytes);
+void lf_free (void *mem);
+void* lf_try_malloc (lf_size_t n_bytes);
+void* lf_try_realloc (void *mem,
+ lf_size_t n_bytes);
+
+/* Convenience memory allocators
+ */
+#define lf_new(struct_type, n_structs) \
+ ((struct_type *) lf_malloc (((lf_size_t) sizeof (struct_type)) * ((lf_size_t) (n_structs))))
+#define lf_new0(struct_type, n_structs) \
+ ((struct_type *) lf_malloc0 (((lf_size_t) sizeof (struct_type)) * ((lf_size_t) (n_structs))))
+#define lf_renew(struct_type, mem, n_structs) \
+ ((struct_type *) lf_realloc ((mem), ((lf_size_t) sizeof (struct_type)) * ((lf_size_t) (n_structs))))
+
+
+/* Memory allocation virtualization, so you can override memory
+ * allocation behavior. lf_mem_set_vtable() has to be the very first
+ * liblf function called if being used
+ */
+typedef struct
+{
+ void* (*malloc) (lf_size_t n_bytes);
+ void* (*realloc) (void *mem,
+ lf_size_t n_bytes);
+ void (*free) (void *mem);
+ /* optional */
+ void* (*calloc) (lf_size_t n_blocks,
+ lf_size_t n_block_bytes);
+ void* (*try_malloc) (lf_size_t n_bytes);
+ void* (*try_realloc) (void *mem,
+ lf_size_t n_bytes);
+ LfPaddingFunc padding1;
+ LfPaddingFunc padding2;
+} LfMemVTable;
+
+void lf_mem_set_vtable (LfMemVTable *vtable);
+lf_bool_t lf_mem_is_system_malloc (void);
+
+typedef lf_bool_t (* LfUtf8ValidateFunc) (const char *str,
+ int max_len);
+
+void lf_set_utf8_validator (LfUtf8ValidateFunc validate_func);
+
+LF_END_DECLS
+
+#endif /* __LF_UTIL_H__ */
diff --git a/libsn/sn-xmessages.c b/libsn/sn-xmessages.c
new file mode 100644
index 0000000..b195b49
--- /dev/null
+++ b/libsn/sn-xmessages.c
@@ -0,0 +1,836 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "lf-xmessages.h"
+#include "lf-list.h"
+#include "lf-internals.h"
+
+typedef struct
+{
+ Display *xdisplay;
+ Atom type_atom;
+ char *message_type;
+ LfXmessageFunc func;
+ void *func_data;
+ LfFreeFunc free_data_func;
+} LfXmessageHandler;
+
+typedef struct
+{
+ Atom type_atom;
+ Window xwindow;
+ char *message;
+ int allocated;
+} LfXmessage;
+
+static LfList *xmessage_funcs = NULL;
+static LfList *pending_messages = NULL;
+
+void
+lf_internal_add_xmessage_func (LfDisplay *display,
+ const char *message_type,
+ LfXmessageFunc func,
+ void *func_data,
+ LfFreeFunc free_data_func)
+{
+ LfXmessageHandler *handler;
+
+ if (xmessage_funcs == NULL)
+ xmessage_funcs = lf_list_new ();
+
+ handler = lf_new0 (LfXmessageHandler, 1);
+
+ handler->xdisplay = lf_display_get_x_display (display);
+ handler->type_atom = lf_internal_atom_get (display, message_type);
+ handler->message_type = lf_internal_strdup (message_type);
+ handler->func= func;
+ handler->func_data = func_data;
+ handler->free_data_func = free_data_func;
+
+ lf_list_prepend (xmessage_funcs, handler);
+}
+
+typedef struct
+{
+ const char *message_type;
+ LfXmessageFunc func;
+ void *func_data;
+ LfXmessageHandler *handler;
+} FindHandlerData;
+
+static lf_bool_t
+find_handler_foreach (void *value,
+ void *data)
+{
+ FindHandlerData *fhd = data;
+ LfXmessageHandler *handler = value;
+
+ if (handler->func == fhd->func &&
+ handler->func_data == fhd->func_data &&
+ strcmp (fhd->message_type, handler->message_type) == 0)
+ {
+ fhd->handler = handler;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+lf_internal_remove_xmessage_func (LfDisplay *display,
+ const char *message_type,
+ LfXmessageFunc func,
+ void *func_data)
+{
+ FindHandlerData fhd;
+
+ fhd.func = func;
+ fhd.func_data = func_data;
+ fhd.handler = NULL;
+
+ if (xmessage_funcs != NULL)
+ lf_list_foreach (xmessage_funcs, find_handler_foreach, &fhd);
+
+ if (fhd.handler != NULL)
+ {
+ lf_list_remove (xmessage_funcs, fhd.handler);
+
+ lf_free (fhd.handler->message_type);
+
+ if (fhd.handler->free_data_func)
+ (* fhd.handler->free_data_func) (fhd.handler->func_data);
+
+ lf_free (fhd.handler);
+ }
+}
+
+void
+lf_internal_broadcast_xmessage (LfDisplay *display,
+ const char *message_type,
+ const char *message)
+{
+ Atom type_atom;
+ Window xwindow;
+ Display *xdisplay;
+
+ if (!lf_internal_utf8_validate (message, -1))
+ {
+ fprintf (stderr,
+ "Attempted to send non-UTF-8 X message: %s\n",
+ message);
+ return;
+ }
+
+ xdisplay = lf_display_get_x_display (display);
+
+ {
+ XSetWindowAttributes attrs;
+
+ attrs.override_redirect = True;
+ attrs.event_mask = PropertyChangeMask | StructureNotifyMask;
+
+ xwindow =
+ XCreateWindow (xdisplay,
+ RootWindow (xdisplay, 0),
+ -100, -100, 1, 1,
+ 0,
+ CopyFromParent,
+ CopyFromParent,
+ CopyFromParent,
+ CWOverrideRedirect | CWEventMask,
+ &attrs);
+ }
+
+ type_atom = lf_internal_atom_get (display, message_type);
+
+ {
+ XEvent xevent;
+ const char *src;
+ const char *src_end;
+ char *dest;
+ char *dest_end;
+
+ xevent.xclient.type = ClientMessage;
+ xevent.xclient.message_type = type_atom;
+ xevent.xclient.display = xdisplay;
+ xevent.xclient.window = xwindow;
+ xevent.xclient.format = 8;
+
+ src = message;
+ src_end = message + strlen (message) + 1; /* +1 to include nul byte */
+
+ while (src != src_end)
+ {
+ dest = &xevent.xclient.data.b[0];
+ dest_end = dest + 20;
+
+ while (dest != dest_end &&
+ src != src_end)
+ {
+ *dest = *src;
+ ++dest;
+ ++src;
+ }
+
+ lf_internal_send_event_all_screens (display, PropertyChangeMask,
+ &xevent);
+ }
+ }
+
+ XDestroyWindow (xdisplay, xwindow);
+ XFlush (xdisplay);
+}
+
+typedef struct
+{
+ Display *xdisplay;
+ Atom atom;
+ lf_bool_t found_handler;
+} HandlerForAtomData;
+
+static lf_bool_t
+handler_for_atom_foreach (void *value,
+ void *data)
+{
+ LfXmessageHandler *handler = value;
+ HandlerForAtomData *hfad = data;
+
+ if (handler->xdisplay == hfad->xdisplay &&
+ handler->type_atom == hfad->atom)
+ {
+ hfad->found_handler = TRUE;
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+static lf_bool_t
+some_handler_handles_event (LfDisplay *display,
+ XEvent *xevent)
+{
+ HandlerForAtomData hfad;
+
+ hfad.atom = xevent->xclient.message_type;
+ hfad.xdisplay = lf_display_get_x_display (display);
+ hfad.found_handler = FALSE;
+
+ if (xmessage_funcs)
+ lf_list_foreach (xmessage_funcs,
+ handler_for_atom_foreach,
+ &hfad);
+
+ return hfad.found_handler;
+}
+
+typedef struct
+{
+ Display *xdisplay;
+ XEvent *xevent;
+ LfXmessage *message;
+} FindMessageData;
+
+static lf_bool_t
+find_message_foreach (void *value,
+ void *data)
+{
+ LfXmessage *message = value;
+ FindMessageData *fmd = data;
+
+ if (fmd->xevent->xclient.window ==
+ message->xwindow &&
+ fmd->xevent->xclient.message_type ==
+ message->type_atom)
+ {
+ fmd->message = message;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static LfXmessage*
+add_event_to_messages (LfDisplay *display,
+ XEvent *xevent)
+{
+ FindMessageData fmd;
+ LfXmessage *message;
+ const char *src;
+ const char *src_end;
+ char *dest;
+ lf_bool_t completed;
+
+ /* We don't want screwy situations to end up causing us to allocate
+ * infinite memory. Cap the length of a message.
+ */
+#define MAX_MESSAGE_LENGTH 4096
+
+ fmd.xdisplay = lf_display_get_x_display (display);
+ fmd.xevent = xevent;
+ fmd.message = NULL;
+
+ if (pending_messages)
+ lf_list_foreach (pending_messages, find_message_foreach, &fmd);
+
+ message = fmd.message;
+
+ if (message == NULL)
+ {
+ /* Create a new message */
+ message = lf_new0 (LfXmessage, 1);
+
+ message->type_atom = xevent->xclient.message_type;
+ message->xwindow = xevent->xclient.window;
+ message->message = NULL;
+ message->allocated = 0;
+
+ if (pending_messages == NULL)
+ pending_messages = lf_list_new ();
+
+ lf_list_prepend (pending_messages, message);
+ }
+
+ if (message->allocated > MAX_MESSAGE_LENGTH)
+ {
+ /* This message is some kind of crap - just dump it. */
+ lf_free (message->message);
+ lf_list_remove (pending_messages, message);
+ lf_free (message);
+ return NULL;
+ }
+
+ src = &xevent->xclient.data.b[0];
+ src_end = src + 20;
+
+ message->message = lf_realloc (message->message,
+ message->allocated + 20);
+ dest = message->message + message->allocated;
+ message->allocated += 20;
+
+ completed = FALSE;
+
+ /* Copy bytes, be sure we get nul byte also */
+ while (src != src_end)
+ {
+ *dest = *src;
+
+ if (*src == '\0')
+ {
+ completed = TRUE;
+ break;
+ }
+
+ ++dest;
+ ++src;
+ }
+
+ if (completed)
+ {
+ /* Pull message out of the pending queue and return it */
+ lf_list_remove (pending_messages, message);
+ return message;
+ }
+ else
+ return NULL;
+}
+
+typedef struct
+{
+ LfDisplay *display;
+ LfXmessage *message;
+} MessageDispatchData;
+
+static lf_bool_t
+dispatch_message_foreach (void *value,
+ void *data)
+{
+ LfXmessageHandler *handler = value;
+ MessageDispatchData *mdd = data;
+
+ (* handler->func) (mdd->display,
+ handler->message_type,
+ mdd->message->message,
+ handler->func_data);
+
+ return TRUE;
+}
+
+lf_bool_t
+lf_internal_xmessage_process_event (LfDisplay *display,
+ XEvent *xevent)
+{
+ lf_bool_t retval;
+ LfXmessage *message;
+
+ retval = FALSE;
+ message = NULL;
+
+ switch (xevent->xany.type)
+ {
+ case ClientMessage:
+ if (some_handler_handles_event (display, xevent))
+ {
+ retval = TRUE;
+
+ message = add_event_to_messages (display, xevent);
+ }
+ break;
+ }
+
+ if (message != NULL)
+ {
+ /* We need to dispatch and free this message; ignore
+ * messages containing invalid UTF-8
+ */
+
+ if (lf_internal_utf8_validate (message->message, -1))
+ {
+ MessageDispatchData mdd;
+
+ mdd.display = display;
+ mdd.message = message;
+
+ /* We could stand to be more reentrant here; it will
+ * barf if you add/remove a handler from inside the
+ * dispatch
+ */
+ if (xmessage_funcs != NULL)
+ lf_list_foreach (xmessage_funcs,
+ dispatch_message_foreach,
+ &mdd);
+ }
+
+ lf_free (message->message);
+ lf_free (message);
+ }
+
+ return retval;
+}
+
+static void
+append_to_string (char **append_to,
+ int *current_len,
+ const char *append)
+{
+ int len;
+ char *end;
+
+ len = strlen (append);
+
+ *append_to = lf_realloc (*append_to, *current_len + len + 1);
+
+ end = *append_to + *current_len;
+ strcpy (end, append);
+ *current_len = *current_len + len;
+}
+
+static void
+append_to_string_escaped (char **append_to,
+ int *current_len,
+ const char *append)
+{
+ char *escaped;
+ int len;
+ char buf[2];
+ const char *p;
+
+ buf[1] = '\0';
+ len = 0;
+ escaped = NULL;
+
+ /* We are the most inefficient algorithm ever! woot! */
+ /* really need GString here */
+ p = append;
+ while (*p)
+ {
+ if (*p == '\\' || *p == '"' || *p == ' ')
+ {
+ buf[0] = '\\';
+ append_to_string (&escaped, &len, buf);
+ }
+ buf[0] = *p;
+ append_to_string (&escaped, &len, buf);
+
+ ++p;
+ }
+
+ append_to_string (append_to, current_len, escaped);
+
+ lf_free (escaped);
+}
+
+char*
+lf_internal_serialize_message (const char *prefix,
+ const char **property_names,
+ const char **property_values)
+{
+ int len;
+ char *retval;
+ int i;
+
+ /* GLib would simplify this a lot... */
+ len = 0;
+ retval = NULL;
+
+ append_to_string (&retval, &len, prefix);
+ append_to_string (&retval, &len, ": ");
+
+ i = 0;
+ while (property_names[i])
+ {
+ append_to_string (&retval, &len, property_names[i]);
+ append_to_string (&retval, &len, "=");
+ append_to_string_escaped (&retval, &len, property_values[i]);
+
+ ++i;
+ }
+
+ return retval;
+}
+
+static void
+append_string_to_list (char ***list,
+ const char *append)
+{
+ if (*list == NULL)
+ {
+ *list = lf_new0 (char*, 2);
+ (*list)[0] = lf_internal_strdup (append);
+ }
+ else
+ {
+ int i;
+
+ i = 0;
+ while ((*list)[i] != NULL)
+ ++i;
+
+ *list = lf_renew (char*, *list, i + 2);
+ (*list)[i] = lf_internal_strdup (append);
+ (*list)[i+1] = NULL;
+ }
+}
+
+static char*
+parse_prefix_up_to (const char *str,
+ int up_to,
+ const char **end)
+{
+ char *prefix;
+ const char *p;
+ int len;
+
+ prefix = NULL;
+ *end = NULL;
+
+ p = str;
+ while (*p && *p != up_to)
+ ++p;
+
+ if (*p == '\0')
+ return NULL;
+
+ len = p - str;
+ prefix = lf_internal_strndup (str, len);
+
+ *end = str + len;
+
+ return prefix;
+}
+
+
+/* Single quotes preserve the literal string exactly. escape
+ * sequences are not allowed; not even \' - if you want a '
+ * in the quoted text, you have to do something like 'foo'\''bar'
+ *
+ * Double quotes allow $ ` " \ and newline to be escaped with backslash.
+ * Otherwise double quotes preserve things literally.
+ *
+ * (This is overkill for X messages, copied from GLib shell code,
+ * copyright Red Hat Inc. also)
+ */
+
+static lf_bool_t
+unquote_string_inplace (char *str,
+ char **end)
+{
+ char* dest;
+ char* s;
+ char quote_char;
+
+ dest = s = str;
+
+ quote_char = *s;
+
+ if (!(*s == '"' || *s == '\''))
+ {
+ /* doesn't begin with quotation mark */
+ *end = str;
+ return FALSE;
+ }
+
+ /* Skip the initial quote mark */
+ ++s;
+
+ if (quote_char == '"')
+ {
+ while (*s)
+ {
+ /* g_assert(s > dest); */ /* loop invariant */
+
+ switch (*s)
+ {
+ case '"':
+ /* End of the string, return now */
+ *dest = '\0';
+ ++s;
+ *end = s;
+ return TRUE;
+ break;
+
+ case '\\':
+ /* Possible escaped quote or \ */
+ ++s;
+ switch (*s)
+ {
+ case '"':
+ case '\\':
+ case '`':
+ case '$':
+ case '\n':
+ *dest = *s;
+ ++s;
+ ++dest;
+ break;
+
+ default:
+ /* not an escaped char */
+ *dest = '\\';
+ ++dest;
+ /* ++s already done. */
+ break;
+ }
+ break;
+
+ default:
+ *dest = *s;
+ ++dest;
+ ++s;
+ break;
+ }
+
+ /* g_assert(s > dest);*/ /* loop invariant */
+ }
+ }
+ else
+ {
+ while (*s)
+ {
+ /* g_assert(s > dest); */ /* loop invariant */
+
+ if (*s == '\'')
+ {
+ /* End of the string, return now */
+ *dest = '\0';
+ ++s;
+ *end = s;
+ return TRUE;
+ }
+ else
+ {
+ *dest = *s;
+ ++dest;
+ ++s;
+ }
+
+ /* g_assert(s > dest); */ /* loop invariant */
+ }
+ }
+
+ /* If we reach here this means the close quote was never encountered */
+
+ *dest = '\0';
+ *end = s;
+ return FALSE;
+}
+
+static lf_bool_t
+unescape_string_inplace (char *str,
+ char **end)
+{
+ char* dest;
+ char* s;
+ lf_bool_t escaped;
+
+ dest = s = str;
+ escaped = FALSE;
+
+ while (*s)
+ {
+ if (escaped)
+ {
+ escaped = FALSE;
+
+ *dest = *s;
+ ++dest;
+ }
+ else
+ {
+ if (*s == ' ')
+ break;
+ else if (*s == '\\')
+ escaped = TRUE;
+ else
+ {
+ *dest = *s;
+ ++dest;
+ }
+ }
+
+ ++s;
+ }
+
+ *dest = '\0';
+ *end = s;
+
+ return TRUE;
+}
+
+static lf_bool_t
+parse_property (const char *str,
+ char **name_p,
+ char **val_p,
+ const char **end_p)
+{
+ char *val;
+ char *name;
+ char *copy;
+ char *p;
+
+ *end_p = NULL;
+
+ copy = lf_internal_strdup (str);
+ p = copy;
+
+ while (*p == ' ')
+ ++p;
+
+ name = parse_prefix_up_to (p, '=', (const char**) &p);
+ if (name == NULL)
+ {
+ lf_free (copy);
+ return FALSE;
+ }
+ ++p; /* skip '=' */
+
+ while (*p == ' ')
+ ++p;
+
+ if (*p == '\'' ||
+ *p == '"')
+ {
+ char *end;
+
+ end = NULL;
+ if (!unquote_string_inplace (p, &end))
+ {
+ lf_free (copy);
+ return FALSE;
+ }
+
+ val = lf_internal_strndup (p, end - p);
+
+ p = end;
+ }
+ else
+ {
+ char *end;
+
+ end = NULL;
+ if (!unescape_string_inplace (p, &end))
+ {
+ lf_free (copy);
+ return FALSE;
+ }
+
+ val = lf_internal_strndup (p, end - p);
+
+ p = end;
+ }
+
+ while (*p == ' ')
+ ++p;
+
+ *end_p = str + (p - copy);
+
+ lf_free (copy);
+
+ *name_p = name;
+ *val_p = val;
+
+ return TRUE;
+}
+
+lf_bool_t
+lf_internal_unserialize_message (const char *message,
+ char **prefix_p,
+ char ***property_names,
+ char ***property_values)
+{
+ /* GLib would simplify this a lot... */
+ char *prefix;
+ char **names;
+ char **values;
+ const char *p;
+ char *name;
+ char *value;
+
+ *prefix_p = NULL;
+ *property_names = NULL;
+ *property_values = NULL;
+
+ prefix = NULL;
+ names = NULL;
+ values = NULL;
+
+ prefix = parse_prefix_up_to (message, ':', &p);
+ if (prefix == NULL)
+ return FALSE;
+
+ ++p; /* skip ':' */
+
+ name = NULL;
+ value = NULL;
+ while (parse_property (p, &name, &value, &p))
+ {
+ append_string_to_list (&names, name);
+ append_string_to_list (&values, value);
+ }
+
+ *prefix_p = prefix;
+ *property_names = names;
+ *property_values = values;
+
+ return TRUE;
+}
diff --git a/libsn/sn-xmessages.h b/libsn/sn-xmessages.h
new file mode 100644
index 0000000..6920d5d
--- /dev/null
+++ b/libsn/sn-xmessages.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __LF_XMESSAGES_H__
+#define __LF_XMESSAGES_H__
+
+#include <liblf/lf-common.h>
+
+LF_BEGIN_DECLS
+
+typedef void (* LfXmessageFunc) (LfDisplay *display,
+ const char *message_type,
+ const char *message,
+ void *user_data);
+
+void lf_internal_add_xmessage_func (LfDisplay *display,
+ const char *message_type,
+ LfXmessageFunc func,
+ void *func_data,
+ LfFreeFunc free_data_func);
+void lf_internal_remove_xmessage_func (LfDisplay *display,
+ const char *message_type,
+ LfXmessageFunc func,
+ void *func_data);
+void lf_internal_broadcast_xmessage (LfDisplay *display,
+ const char *message_type,
+ const char *message);
+
+char* lf_internal_serialize_message (const char *prefix,
+ const char **property_names,
+ const char **property_values);
+lf_bool_t lf_internal_unserialize_message (const char *message,
+ char **prefix,
+ char ***property_names,
+ char ***property_values);
+
+LF_END_DECLS
+
+#endif /* __LF_XMESSAGES_H__ */
diff --git a/libsn/sn-xutils.c b/libsn/sn-xutils.c
new file mode 100644
index 0000000..1a9d187
--- /dev/null
+++ b/libsn/sn-xutils.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "lf-internals.h"
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+Atom
+lf_internal_atom_get (LfDisplay *display,
+ const char *atom_name)
+{
+ return XInternAtom (lf_display_get_x_display (display),
+ atom_name,
+ False);
+}
+
+void
+lf_internal_set_utf8_string (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ const char *str)
+{
+ lf_display_error_trap_push (display);
+
+ XChangeProperty (lf_display_get_x_display (display),
+ xwindow,
+ lf_internal_atom_get (display, property),
+ lf_internal_atom_get (display, "UTF8_STRING"),
+ 8, PropModeReplace, (unsigned char*) str,
+ strlen (str));
+
+ lf_display_error_trap_pop (display);
+}
+
+void
+lf_internal_set_string (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ const char *str)
+{
+ lf_display_error_trap_push (display);
+
+ XChangeProperty (lf_display_get_x_display (display),
+ xwindow,
+ lf_internal_atom_get (display, property),
+ XA_STRING,
+ 8, PropModeReplace, (unsigned char*) str,
+ strlen (str));
+
+ lf_display_error_trap_pop (display);
+}
+
+void
+lf_internal_set_cardinal (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ int val)
+{
+ unsigned long long_val = val;
+
+ lf_display_error_trap_push (display);
+
+ XChangeProperty (lf_display_get_x_display (display),
+ xwindow,
+ lf_internal_atom_get (display, property),
+ XA_CARDINAL,
+ 32, PropModeReplace, (unsigned char*) &long_val, 1);
+
+ lf_display_error_trap_pop (display);
+}
+
+void
+lf_internal_set_window (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ Window val)
+{
+ lf_display_error_trap_push (display);
+
+ XChangeProperty (lf_display_get_x_display (display),
+ xwindow,
+ lf_internal_atom_get (display, property),
+ XA_WINDOW,
+ 32, PropModeReplace, (unsigned char*) &val, 1);
+
+ lf_display_error_trap_pop (display);
+}
+
+void
+lf_internal_set_cardinal_list (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ int *vals,
+ int n_vals)
+{
+ lf_display_error_trap_push (display);
+
+ XChangeProperty (lf_display_get_x_display (display),
+ xwindow,
+ lf_internal_atom_get (display, property),
+ XA_CARDINAL,
+ 32, PropModeReplace, (unsigned char*) vals, n_vals);
+
+ lf_display_error_trap_pop (display);
+}
+
+void
+lf_internal_set_atom_list (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ Atom *vals,
+ int n_vals)
+{
+ lf_display_error_trap_push (display);
+
+ XChangeProperty (lf_display_get_x_display (display),
+ xwindow,
+ lf_internal_atom_get (display, property),
+ XA_ATOM,
+ 32, PropModeReplace, (unsigned char*) vals, n_vals);
+
+ lf_display_error_trap_pop (display);
+}
+
+lf_bool_t
+lf_internal_get_cardinal (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ int *val)
+{
+ Atom type;
+ int format;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ unsigned long *num;
+ int result;
+
+ *val = 0;
+
+ lf_display_error_trap_push (display);
+ type = None;
+ num = NULL;
+ result = XGetWindowProperty (lf_display_get_x_display (display),
+ xwindow,
+ lf_internal_atom_get (display, property),
+ 0, 256, /* 256 = random number */
+ False, XA_CARDINAL, &type, &format, &nitems,
+ &bytes_after, (unsigned char **)&num);
+ lf_display_error_trap_pop (display);
+ if (result != Success || num == NULL || nitems == 0)
+ {
+ if (num)
+ XFree (num);
+ return FALSE;
+ }
+
+ if (type != XA_CARDINAL)
+ {
+ XFree (num);
+ return FALSE;
+ }
+
+ *val = *num;
+ XFree (num);
+
+ return TRUE;
+}
+
+lf_bool_t
+lf_internal_get_utf8_string (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ char **val)
+{
+ Atom type;
+ int format;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ char *str;
+ int result;
+ Atom utf8_string;
+
+ utf8_string = lf_internal_atom_get (display, "UTF8_STRING");
+
+ *val = NULL;
+
+ lf_display_error_trap_push (display);
+ type = None;
+ str = NULL;
+ result = XGetWindowProperty (lf_display_get_x_display (display),
+ xwindow,
+ lf_internal_atom_get (display, property),
+ 0, 20000, /* 20000 = random number */
+ False,
+ utf8_string,
+ &type, &format, &nitems,
+ &bytes_after, (unsigned char **)&str);
+ lf_display_error_trap_pop (display);
+ if (result != Success || str == NULL)
+ {
+ if (str)
+ XFree (str);
+ return FALSE;
+ }
+
+ if (type != utf8_string ||
+ format != 8 ||
+ nitems == 0)
+ {
+ XFree (str);
+ return FALSE;
+ }
+
+ if (!lf_internal_utf8_validate (str, nitems))
+ {
+ fprintf (stderr, "Warning: invalid UTF-8 in property %s on window 0x%lx\n",
+ property, xwindow);
+ XFree (str);
+ return FALSE;
+ }
+
+ *val = lf_internal_strdup (str);
+ XFree (str);
+
+ return TRUE;
+}
+
+lf_bool_t
+lf_internal_get_string (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ char **val)
+{
+ Atom type;
+ int format;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ char *str;
+ int result;
+
+ *val = NULL;
+
+ lf_display_error_trap_push (display);
+ type = None;
+ str = NULL;
+ result = XGetWindowProperty (lf_display_get_x_display (display),
+ xwindow,
+ lf_internal_atom_get (display, property),
+ 0, 20000, /* 20000 = random number */
+ False,
+ XA_STRING,
+ &type, &format, &nitems,
+ &bytes_after, (unsigned char **)&str);
+ lf_display_error_trap_pop (display);
+ if (result != Success || str == NULL)
+ {
+ if (str)
+ XFree (str);
+ return FALSE;
+ }
+
+ if (type != XA_STRING ||
+ format != 8 ||
+ nitems == 0)
+ {
+ XFree (str);
+ return FALSE;
+ }
+
+ *val = lf_internal_strdup (str);
+ XFree (str);
+
+ return TRUE;
+}
+
+lf_bool_t
+lf_internal_get_window (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ Window *val)
+{
+ Atom type;
+ int format;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ Window *win;
+ int result;
+
+ *val = 0;
+
+ lf_display_error_trap_push (display);
+ type = None;
+ win = NULL;
+ result = XGetWindowProperty (lf_display_get_x_display (display),
+ xwindow,
+ lf_internal_atom_get (display, property),
+ 0, 256, /* 256 = random number */
+ False, XA_WINDOW, &type, &format, &nitems,
+ &bytes_after, (unsigned char **)&win);
+ lf_display_error_trap_pop (display);
+ if (result != Success || win == NULL || nitems == 0)
+ {
+ if (win)
+ XFree (win);
+ return FALSE;
+ }
+
+ if (type != XA_WINDOW)
+ {
+ XFree (win);
+ return FALSE;
+ }
+
+ *val = *win;
+ XFree (win);
+
+ return TRUE;
+}
+
+lf_bool_t
+lf_internal_get_atom_list (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ Atom **atoms,
+ int *n_atoms)
+{
+ Atom type;
+ int format;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ Atom *data;
+ int result;
+
+ *atoms = NULL;
+ *n_atoms = 0;
+ data = NULL;
+
+ lf_display_error_trap_push (display);
+ type = None;
+ result = XGetWindowProperty (lf_display_get_x_display (display),
+ xwindow,
+ lf_internal_atom_get (display, property),
+ 0, 1000, /* 1000 = random number */
+ False, XA_ATOM, &type, &format, &nitems,
+ &bytes_after, (unsigned char **)&data);
+ lf_display_error_trap_pop (display);
+ if (result != Success || data == NULL)
+ {
+ if (data)
+ XFree (data);
+ return FALSE;
+ }
+
+ if (type != XA_ATOM)
+ {
+ XFree (data);
+ return FALSE;
+ }
+
+ *atoms = lf_new (Atom, nitems);
+ memcpy (*atoms, data, sizeof (Atom) * nitems);
+ *n_atoms = nitems;
+
+ XFree (data);
+
+ return TRUE;
+}
+
+lf_bool_t
+lf_internal_get_cardinal_list (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ int **vals,
+ int *n_vals)
+{
+ Atom type;
+ int format;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ unsigned long *nums;
+ int result;
+ int i;
+
+ *vals = NULL;
+ *n_vals = 0;
+ nums = NULL;
+
+ lf_display_error_trap_push (display);
+ type = None;
+ result = XGetWindowProperty (lf_display_get_x_display (display),
+ xwindow,
+ lf_internal_atom_get (display, property),
+ 0, 1000, /* 1000 = random number */
+ False, XA_CARDINAL, &type, &format, &nitems,
+ &bytes_after, (unsigned char **)&nums);
+ lf_display_error_trap_pop (display);
+ if (result != Success || nums == NULL)
+ {
+ if (nums)
+ XFree (nums);
+ return FALSE;
+ }
+
+ if (type != XA_CARDINAL)
+ {
+ XFree (nums);
+ return FALSE;
+ }
+
+ *vals = lf_new (int, nitems);
+ *n_vals = nitems;
+ i = 0;
+ while (i < *n_vals)
+ {
+ (*vals)[i] = nums[i];
+ ++i;
+ }
+
+ XFree (nums);
+
+ return TRUE;
+}
+
+void
+lf_internal_send_event_all_screens (LfDisplay *display,
+ unsigned long mask,
+ XEvent *xevent)
+{
+ int i;
+ Display *xdisplay;
+
+ xdisplay = lf_display_get_x_display (display);
+
+ i = 0;
+ while (lf_display_get_x_screen (display, i) != NULL)
+ {
+ XSendEvent (xdisplay,
+ RootWindow (xdisplay, i),
+ False,
+ mask,
+ xevent);
+
+ ++i;
+ }
+}
diff --git a/libsn/sn-xutils.h b/libsn/sn-xutils.h
new file mode 100644
index 0000000..819a3c8
--- /dev/null
+++ b/libsn/sn-xutils.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __LF_XUTILS_H__
+#define __LF_XUTILS_H__
+
+#include <liblf/lf-common.h>
+
+LF_BEGIN_DECLS
+
+Atom lf_internal_atom_get (LfDisplay *display,
+ const char *atom_name);
+void lf_internal_set_utf8_string (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ const char *str);
+void lf_internal_set_string (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ const char *str);
+void lf_internal_set_cardinal (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ int val);
+void lf_internal_set_window (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ Window val);
+void lf_internal_set_cardinal_list (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ int *vals,
+ int n_vals);
+void lf_internal_set_atom_list (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ Atom *vals,
+ int n_vals);
+
+
+lf_bool_t lf_internal_get_utf8_string (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ char **val);
+lf_bool_t lf_internal_get_string (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ char **val);
+lf_bool_t lf_internal_get_cardinal (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ int *val);
+lf_bool_t lf_internal_get_window (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ Window *val);
+lf_bool_t lf_internal_get_atom_list (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ Atom **atoms,
+ int *n_atoms);
+lf_bool_t lf_internal_get_cardinal_list (LfDisplay *display,
+ Window xwindow,
+ const char *property,
+ int **vals,
+ int *n_vals);
+
+void lf_internal_send_event_all_screens (LfDisplay *display,
+ unsigned long mask,
+ XEvent *xevent);
+
+
+LF_END_DECLS
+
+#endif /* __LF_XUTILS_H__ */