#include "config.h"
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
+#include "gdkdisplay-x11.h"
-#include <glib.h>
#include "gdkx.h"
#include "gdkasync.h"
#include "gdkdisplay.h"
-#include "gdkdisplay-x11.h"
#include "gdkeventsource.h"
#include "gdkeventtranslator.h"
#include "gdkscreen.h"
#include "gdkdevicemanager.h"
#include "xsettings-client.h"
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
#include <X11/Xatom.h>
#ifdef HAVE_XKB
#include <X11/extensions/Xrandr.h>
#endif
+typedef struct _GdkErrorTrap GdkErrorTrap;
+
+struct _GdkErrorTrap
+{
+ /* Next sequence when trap was pushed, i.e. first sequence to
+ * ignore
+ */
+ gulong start_sequence;
+
+ /* Next sequence when trap was popped, i.e. first sequence
+ * to not ignore. 0 if trap is still active.
+ */
+ gulong end_sequence;
+
+ /* Most recent error code within the sequence */
+ int error_code;
+};
static void gdk_display_x11_dispose (GObject *object);
static void gdk_display_x11_finalize (GObject *object);
0, G_MAXLONG, False, XA_CARDINAL, &type,
&format, &nitems,
&bytes_after, &data);
- gdk_error_trap_pop ();
+ gdk_error_trap_pop_ignored ();
if (type != None)
{
gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"),
0, G_MAXLONG, False, XA_ATOM, &type, &format, &nitems,
&bytes_after, &data);
- gdk_error_trap_pop ();
+ gdk_error_trap_pop_ignored ();
if (type != None)
{
event->configure.x = tx;
event->configure.y = ty;
}
- gdk_error_trap_pop ();
+ gdk_error_trap_pop_ignored ();
}
else
{
if (device->source == GDK_SOURCE_KEYBOARD)
continue;
- display_x11->input_devices = g_list_prepend (display_x11->input_devices, l->data);
+ display_x11->input_devices = g_list_prepend (display_x11->input_devices,
+ g_object_ref (l->data));
}
g_list_free (list);
}
/* Add the core pointer to the devices list */
- display_x11->input_devices = g_list_prepend (display_x11->input_devices, display->core_pointer);
+ display_x11->input_devices = g_list_prepend (display_x11->input_devices,
+ g_object_ref (display->core_pointer));
g_list_free (list);
}
XQueryPointer (display_x11->xdisplay,
GDK_SCREEN_X11 (display_x11->default_screen)->xroot_window,
&root, &child, &rootx, &rooty, &winx, &winy, &xmask);
- gdk_flush ();
if (G_UNLIKELY (gdk_error_trap_pop () == BadWindow))
{
g_warning ("Connection to display %s appears to be untrusted. Pointer and keyboard grabs and inter-client communication may not work as expected.", gdk_display_get_name (display));
_gdk_x11_screen_setup (display_x11->screens[i]);
g_signal_emit_by_name (display, "opened");
- g_signal_emit_by_name (gdk_display_manager_get(),
- "display_opened", display);
+ g_signal_emit_by_name (gdk_display_manager_get (), "display-opened", display);
return display;
}
XCloseDisplay (display_x11->xdisplay);
+ /* error traps */
+ while (display_x11->error_traps != NULL)
+ {
+ GdkErrorTrap *trap = display_x11->error_traps->data;
+
+ display_x11->error_traps =
+ g_slist_delete_link (display_x11->error_traps,
+ display_x11->error_traps);
+
+ if (trap->end_sequence == 0)
+ g_warning ("Display finalized with an unpopped error trap");
+
+ g_slice_free (GdkErrorTrap, trap);
+ }
+
G_OBJECT_CLASS (_gdk_display_x11_parent_class)->finalize (object);
}
const gchar *startup_id;
if (!display)
- {
- gdk_display = NULL;
- return;
- }
-
- gdk_display = GDK_DISPLAY_XDISPLAY (display);
+ return;
g_free (display_x11->startup_notification_id);
display_x11->startup_notification_id = NULL;
startup_id = g_getenv ("DESKTOP_STARTUP_ID");
if (startup_id && *startup_id != '\0')
{
- gchar *time_str;
-
if (!g_utf8_validate (startup_id, -1, NULL))
- g_warning ("DESKTOP_STARTUP_ID contains invalid UTF-8");
+ g_warning ("DESKTOP_STARTUP_ID contains invalid UTF-8");
else
- display_x11->startup_notification_id = g_strdup (startup_id);
-
- /* Find the launch time from the startup_id, if it's there. Newer spec
- * states that the startup_id is of the form <unique>_TIME<timestamp>
- */
- time_str = g_strrstr (startup_id, "_TIME");
- if (time_str != NULL)
- {
- gulong retval;
- gchar *end;
- errno = 0;
-
- /* Skip past the "_TIME" part */
- time_str += 5;
-
- retval = strtoul (time_str, &end, 0);
- if (end != time_str && errno == 0)
- display_x11->user_time = retval;
- }
+ gdk_x11_display_set_startup_notification_id (display, startup_id);
/* Clear the environment variable so it won't be inherited by
* child processes and confuse things.
*/
g_unsetenv ("DESKTOP_STARTUP_ID");
-
- /* Set the startup id on the leader window so it
- * applies to all windows we create on this display
- */
- XChangeProperty (display_x11->xdisplay,
- display_x11->leader_window,
- gdk_x11_get_xatom_by_name_for_display (display, "_NET_STARTUP_ID"),
- gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8,
- PropModeReplace,
- (guchar *)startup_id, strlen (startup_id));
}
}
GDK_WINDOW_XID (clipboard_window), time_);
}
- gdk_error_trap_pop ();
+ gdk_error_trap_pop_ignored ();
}
return GDK_DISPLAY_X11 (display)->startup_notification_id;
}
+/**
+ * gdk_x11_display_set_startup_notification_id:
+ * @display: a #GdkDisplay
+ * @startup_id: the startup notification ID (must be valid utf8)
+ *
+ * Sets the startup notification ID for a display.
+ *
+ * This is usually taken from the value of the DESKTOP_STARTUP_ID
+ * environment variable, but in some cases (such as the application not
+ * being launched using exec()) it can come from other sources.
+ *
+ * If the ID contains the string "_TIME" then the portion following that
+ * string is taken to be the X11 timestamp of the event that triggered
+ * the application to be launched and the GDK current event time is set
+ * accordingly.
+ *
+ * The startup ID is also what is used to signal that the startup is
+ * complete (for example, when opening a window or when calling
+ * gdk_notify_startup_complete()).
+ *
+ * Since: 3.0
+ **/
+void
+gdk_x11_display_set_startup_notification_id (GdkDisplay *display,
+ const gchar *startup_id)
+{
+ GdkDisplayX11 *display_x11;
+ gchar *time_str;
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ g_free (display_x11->startup_notification_id);
+ display_x11->startup_notification_id = g_strdup (startup_id);
+
+ /* Find the launch time from the startup_id, if it's there. Newer spec
+ * states that the startup_id is of the form <unique>_TIME<timestamp>
+ */
+ time_str = g_strrstr (startup_id, "_TIME");
+ if (time_str != NULL)
+ {
+ gulong retval;
+ gchar *end;
+ errno = 0;
+
+ /* Skip past the "_TIME" part */
+ time_str += 5;
+
+ retval = strtoul (time_str, &end, 0);
+ if (end != time_str && errno == 0)
+ display_x11->user_time = retval;
+ }
+
+ /* Set the startup id on the leader window so it
+ * applies to all windows we create on this display
+ */
+ XChangeProperty (display_x11->xdisplay,
+ display_x11->leader_window,
+ gdk_x11_get_xatom_by_name_for_display (display, "_NET_STARTUP_ID"),
+ gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8,
+ PropModeReplace,
+ (guchar *)startup_id, strlen (startup_id));
+}
+
/**
* gdk_display_supports_composite:
* @display: a #GdkDisplay
* Returns the list of available input devices attached to @display.
* The list is statically allocated and should not be freed.
*
- * Return value: a list of #GdkDevice
+ * Return value: (transfer none) (element-type GdkDevice):
+ * a list of #GdkDevice
*
* Since: 2.2
*
display_x11->event_types = g_slist_prepend (display_x11->event_types, event_type);
}
+
+/* compare X sequence numbers handling wraparound */
+#define SEQUENCE_COMPARE(a,op,b) (((long) (a) - (long) (b)) op 0)
+
+/* delivers an error event from the error handler in gdkmain-x11.c */
+void
+_gdk_x11_display_error_event (GdkDisplay *display,
+ XErrorEvent *error)
+{
+ GdkDisplayX11 *display_x11;
+ GSList *tmp_list;
+ gboolean ignore;
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ ignore = FALSE;
+ for (tmp_list = display_x11->error_traps;
+ tmp_list != NULL;
+ tmp_list = tmp_list->next)
+ {
+ GdkErrorTrap *trap;
+
+ trap = tmp_list->data;
+
+ if (SEQUENCE_COMPARE (trap->start_sequence, <=, error->serial) &&
+ (trap->end_sequence == 0 ||
+ SEQUENCE_COMPARE (trap->end_sequence, >, error->serial)))
+ {
+ ignore = TRUE;
+ trap->error_code = error->error_code;
+ break; /* only innermost trap gets the error code */
+ }
+ }
+
+ if (!ignore)
+ {
+ gchar buf[64];
+ gchar *msg;
+
+ XGetErrorText (display_x11->xdisplay, error->error_code, buf, 63);
+
+ msg =
+ g_strdup_printf ("The program '%s' received an X Window System error.\n"
+ "This probably reflects a bug in the program.\n"
+ "The error was '%s'.\n"
+ " (Details: serial %ld error_code %d request_code %d minor_code %d)\n"
+ " (Note to programmers: normally, X errors are reported asynchronously;\n"
+ " that is, you will receive the error a while after causing it.\n"
+ " To debug your program, run it with the --sync command line\n"
+ " option to change this behavior. You can then get a meaningful\n"
+ " backtrace from your debugger if you break on the gdk_x_error() function.)",
+ g_get_prgname (),
+ buf,
+ error->serial,
+ error->error_code,
+ error->request_code,
+ error->minor_code);
+
+#ifdef G_ENABLE_DEBUG
+ g_error ("%s", msg);
+#else /* !G_ENABLE_DEBUG */
+ g_warning ("%s\n", msg);
+
+ exit (1);
+#endif /* G_ENABLE_DEBUG */
+ }
+}
+
+static void
+delete_outdated_error_traps (GdkDisplayX11 *display_x11)
+{
+ GSList *tmp_list;
+ gulong processed_sequence;
+
+ processed_sequence = XLastKnownRequestProcessed (display_x11->xdisplay);
+
+ tmp_list = display_x11->error_traps;
+ while (tmp_list != NULL)
+ {
+ GdkErrorTrap *trap = tmp_list->data;
+
+ if (trap->end_sequence != 0 &&
+ SEQUENCE_COMPARE (trap->end_sequence, <=, processed_sequence))
+ {
+ GSList *free_me = tmp_list;
+
+ tmp_list = tmp_list->next;
+ display_x11->error_traps =
+ g_slist_delete_link (display_x11->error_traps, free_me);
+ g_slice_free (GdkErrorTrap, trap);
+ }
+ else
+ {
+ tmp_list = tmp_list->next;
+ }
+ }
+}
+
+/**
+ * gdk_x11_display_error_trap_push:
+ * @display: a #GdkDisplay
+ *
+ * Begins a range of X requests on @display for which X error events
+ * will be ignored. Unignored errors (when no trap is pushed) will abort
+ * the application. Use gdk_x11_display_error_trap_pop() or
+ * gdk_x11_display_error_trap_pop_ignored()to lift a trap pushed
+ * with this function.
+ *
+ * See also gdk_error_trap_push() to push a trap on all displays.
+ *
+ * Since: 3.0
+ */
+void
+gdk_x11_display_error_trap_push (GdkDisplay *display)
+{
+ GdkDisplayX11 *display_x11;
+ GdkErrorTrap *trap;
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ delete_outdated_error_traps (display_x11);
+
+ /* set up the Xlib callback to tell us about errors */
+ _gdk_x11_error_handler_push ();
+
+ trap = g_slice_new0 (GdkErrorTrap);
+
+ trap->start_sequence = XNextRequest (display_x11->xdisplay);
+ trap->error_code = Success;
+
+ display_x11->error_traps =
+ g_slist_prepend (display_x11->error_traps, trap);
+}
+
+static gint
+gdk_x11_display_error_trap_pop_internal (GdkDisplay *display,
+ gboolean need_code)
+{
+ GdkDisplayX11 *display_x11;
+ GdkErrorTrap *trap;
+ GSList *tmp_list;
+ int result;
+
+ display_x11 = GDK_DISPLAY_X11 (display);
+
+ g_return_val_if_fail (display_x11->error_traps != NULL, Success);
+
+ /* Find the first trap that hasn't been popped already */
+ trap = NULL; /* quiet gcc */
+ for (tmp_list = display_x11->error_traps;
+ tmp_list != NULL;
+ tmp_list = tmp_list->next)
+ {
+ trap = tmp_list->data;
+
+ if (trap->end_sequence == 0)
+ break;
+ }
+
+ g_return_val_if_fail (trap != NULL, Success);
+ g_assert (trap->end_sequence == 0);
+
+ /* May need to sync to fill in trap->error_code if we care about
+ * getting an error code.
+ */
+ if (need_code)
+ {
+ gulong processed_sequence;
+ gulong next_sequence;
+
+ next_sequence = XNextRequest (display_x11->xdisplay);
+ processed_sequence = XLastKnownRequestProcessed (display_x11->xdisplay);
+
+ /* If our last request was already processed, there is no point
+ * in syncing. i.e. if last request was a round trip (or even if
+ * we got an event with the serial of a non-round-trip)
+ */
+ if ((next_sequence - 1) != processed_sequence)
+ {
+ XSync (display_x11->xdisplay, False);
+ }
+
+ result = trap->error_code;
+ }
+ else
+ {
+ result = Success;
+ }
+
+ /* record end of trap, giving us a range of
+ * error sequences we'll ignore.
+ */
+ trap->end_sequence = XNextRequest (display_x11->xdisplay);
+
+ /* remove the Xlib callback */
+ _gdk_x11_error_handler_pop ();
+
+ /* we may already be outdated */
+ delete_outdated_error_traps (display_x11);
+
+ return result;
+}
+
+/**
+ * gdk_x11_display_error_trap_pop:
+ * @display: the display
+ *
+ * Pops the error trap pushed by gdk_x11_display_error_trap_push().
+ * Will XSync() if necessary and will always block until
+ * the error is known to have occurred or not occurred,
+ * so the error code can be returned.
+ *
+ * If you don't need to use the return value,
+ * gdk_x11_display_error_trap_pop_ignored() would be more efficient.
+ *
+ * See gdk_error_trap_pop() for the all-displays-at-once
+ * equivalent.
+ *
+ * Since: 3.0
+ *
+ * Return value: X error code or 0 on success
+ */
+gint
+gdk_x11_display_error_trap_pop (GdkDisplay *display)
+{
+ g_return_val_if_fail (GDK_IS_DISPLAY_X11 (display), Success);
+
+ return gdk_x11_display_error_trap_pop_internal (display, TRUE);
+}
+
+/**
+ * gdk_x11_display_error_trap_pop_ignored:
+ * @display: the display
+ *
+ * Pops the error trap pushed by gdk_x11_display_error_trap_push().
+ * Does not block to see if an error occurred; merely records the
+ * range of requests to ignore errors for, and ignores those errors
+ * if they arrive asynchronously.
+ *
+ * See gdk_error_trap_pop_ignored() for the all-displays-at-once
+ * equivalent.
+ *
+ * Since: 3.0
+ */
+void
+gdk_x11_display_error_trap_pop_ignored (GdkDisplay *display)
+{
+ g_return_if_fail (GDK_IS_DISPLAY_X11 (display));
+
+ gdk_x11_display_error_trap_pop_internal (display, FALSE);
+}