Index: gdk/gdkwindow.c =================================================================== RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.c,v retrieving revision 1.163 diff -u -r1.163 gdkwindow.c --- gdk/gdkwindow.c 5 Oct 2004 23:05:13 -0000 1.163 +++ gdk/gdkwindow.c 18 Nov 2004 15:58:08 -0000 @@ -320,6 +320,7 @@ GdkWindowObject *private; GdkWindowObject *temp_private; GdkWindow *temp_window; + GdkScreen *screen; GList *children; GList *tmp; @@ -332,6 +333,14 @@ switch (GDK_WINDOW_TYPE (window)) { + case GDK_WINDOW_ROOT: + screen = gdk_drawable_get_screen (GDK_DRAWABLE (window)); + if (!screen->closed) + { + g_error ("attempted to destroy root window"); + break; + } + /* else fall thru */ case GDK_WINDOW_TOPLEVEL: case GDK_WINDOW_CHILD: case GDK_WINDOW_DIALOG: @@ -406,10 +415,6 @@ gdk_drawable_set_colormap (GDK_DRAWABLE (window), NULL); } - break; - - case GDK_WINDOW_ROOT: - g_error ("attempted to destroy root window"); break; } } Index: gdk/gdkwindow.h =================================================================== RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.h,v retrieving revision 1.51 diff -u -r1.51 gdkwindow.h --- gdk/gdkwindow.h 11 Jul 2004 13:26:57 -0000 1.51 +++ gdk/gdkwindow.h 18 Nov 2004 15:58:08 -0000 @@ -567,6 +567,10 @@ void gdk_window_enable_synchronized_configure (GdkWindow *window); void gdk_window_configure_finished (GdkWindow *window); +void gdk_window_set_allow_screen_change (GdkWindow *window, + gboolean allow); +gboolean gdk_window_get_allow_screen_change (GdkWindow *window); + #ifndef GDK_MULTIHEAD_SAFE GdkPointerHooks *gdk_set_pointer_hooks (const GdkPointerHooks *new_hooks); #endif /* GDK_MULTIHEAD_SAFE */ Index: gdk/x11/gdkdisplay-x11.c =================================================================== RCS file: /cvs/gnome/gtk+/gdk/x11/gdkdisplay-x11.c,v retrieving revision 1.51 diff -u -r1.51 gdkdisplay-x11.c --- gdk/x11/gdkdisplay-x11.c 28 Oct 2004 15:00:04 -0000 1.51 +++ gdk/x11/gdkdisplay-x11.c 18 Nov 2004 15:58:08 -0000 @@ -702,10 +702,9 @@ for (i = 0; i < ScreenCount (display_x11->xdisplay); i++) _gdk_screen_close (display_x11->screens[i]); - g_source_destroy (display_x11->event_source); - - XCloseDisplay (display_x11->xdisplay); - display_x11->xdisplay = NULL; + if (display_x11->event_source) + g_source_destroy (display_x11->event_source); + display_x11->event_source = NULL; G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -753,6 +752,9 @@ g_object_unref (display_x11->screens[i]); g_free (display_x11->screens); g_free (display_x11->startup_notification_id); + + XCloseDisplay (display_x11->xdisplay); + display_x11->xdisplay = NULL; G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -829,10 +831,13 @@ GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display); const gchar *startup_id; - if (display) - gdk_display = GDK_DISPLAY_XDISPLAY (display); - else - gdk_display = NULL; + if (!display) + { + gdk_display = NULL; + return; + } + + gdk_display = GDK_DISPLAY_XDISPLAY (display); g_free (display_x11->startup_notification_id); display_x11->startup_notification_id = NULL; Index: gdk/x11/gdkevents-x11.c =================================================================== RCS file: /cvs/gnome/gtk+/gdk/x11/gdkevents-x11.c,v retrieving revision 1.143 diff -u -r1.143 gdkevents-x11.c --- gdk/x11/gdkevents-x11.c 17 Nov 2004 00:55:10 -0000 1.143 +++ gdk/x11/gdkevents-x11.c 18 Nov 2004 15:58:08 -0000 @@ -192,7 +192,8 @@ { GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen); - xsettings_client_destroy (screen_x11->xsettings_client); + if (screen_x11->xsettings_client) + xsettings_client_destroy (screen_x11->xsettings_client); screen_x11->xsettings_client = NULL; } Index: gdk/x11/gdkscreen-x11.c =================================================================== RCS file: /cvs/gnome/gtk+/gdk/x11/gdkscreen-x11.c,v retrieving revision 1.36 diff -u -r1.36 gdkscreen-x11.c --- gdk/x11/gdkscreen-x11.c 28 Oct 2004 15:00:04 -0000 1.36 +++ gdk/x11/gdkscreen-x11.c 18 Nov 2004 15:58:08 -0000 @@ -286,10 +286,12 @@ _gdk_x11_events_uninit_screen (GDK_SCREEN (object)); - g_object_unref (screen_x11->default_colormap); + if (screen_x11->default_colormap) + g_object_unref (screen_x11->default_colormap); screen_x11->default_colormap = NULL; - screen_x11->root_window = NULL; + if (screen_x11->root_window) + _gdk_window_destroy (screen_x11->root_window, TRUE); screen_x11->xdisplay = NULL; screen_x11->xscreen = NULL; @@ -305,7 +307,8 @@ { GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (object); /* int i; */ - g_object_unref (screen_x11->root_window); + if (screen_x11->root_window) + g_object_unref (screen_x11->root_window); /* Visual Part (Need to implement finalize for Visuals for a clean * finalize) */ Index: gdk/x11/gdkwindow-x11.c =================================================================== RCS file: /cvs/gnome/gtk+/gdk/x11/gdkwindow-x11.c,v retrieving revision 1.227 diff -u -r1.227 gdkwindow-x11.c --- gdk/x11/gdkwindow-x11.c 17 Nov 2004 00:55:10 -0000 1.227 +++ gdk/x11/gdkwindow-x11.c 18 Nov 2004 15:58:09 -0000 @@ -474,7 +474,8 @@ set_wm_protocols (GdkWindow *window) { GdkDisplay *display = gdk_drawable_get_display (window); - Atom protocols[4]; + GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window); + Atom protocols[5]; int n = 0; protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW"); @@ -486,6 +487,9 @@ protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_SYNC_REQUEST"); #endif + if (toplevel && toplevel->allow_screen_change) + protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_CHANGE_DISPLAY"); + XSetWMProtocols (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window), protocols, n); } @@ -584,7 +588,12 @@ GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (GDK_WINDOW_SCREEN (parent)); XSizeHints size_hints; long pid; + gchar *allow_screen_change; + allow_screen_change = g_getenv ("_GTK_ALLOW_SCREEN_CHANGE"); + if (allow_screen_change && strcmp (allow_screen_change, "1") == 0) + toplevel->allow_screen_change = TRUE; + if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_DIALOG) XSetTransientForHint (xdisplay, xid, xparent); @@ -5801,4 +5810,34 @@ } } #endif +} + +void +gdk_window_set_allow_screen_change (GdkWindow *window, + gboolean allow) +{ + GdkToplevelX11 *toplevel; + + g_return_if_fail (GDK_IS_WINDOW (window)); + + toplevel = _gdk_x11_window_get_toplevel (window); + + if (toplevel) + { + toplevel->allow_screen_change = allow; + + set_wm_protocols (window); + } +} + +gboolean +gdk_window_get_allow_screen_change (GdkWindow *window) +{ + GdkToplevelX11 *toplevel; + + g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE); + + toplevel = _gdk_x11_window_get_toplevel (window); + + return toplevel ? toplevel->allow_screen_change: FALSE; } Index: gdk/x11/gdkwindow-x11.h =================================================================== RCS file: /cvs/gnome/gtk+/gdk/x11/gdkwindow-x11.h,v retrieving revision 1.18 diff -u -r1.18 gdkwindow-x11.h --- gdk/x11/gdkwindow-x11.h 18 Oct 2004 21:02:37 -0000 1.18 +++ gdk/x11/gdkwindow-x11.h 18 Nov 2004 15:58:09 -0000 @@ -114,6 +114,8 @@ guint have_fullscreen : 1; /* _NET_WM_STATE_FULLSCREEN */ guint is_leader : 1; + + guint allow_screen_change : 1; /* _NET_CHANGE_DISPLAY */ gulong map_serial; /* Serial of last transition from unmapped */ Index: gtk/gtkmarshalers.list =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkmarshalers.list,v retrieving revision 1.60 diff -u -r1.60 gtkmarshalers.list --- gtk/gtkmarshalers.list 27 Aug 2004 02:54:12 -0000 1.60 +++ gtk/gtkmarshalers.list 18 Nov 2004 15:58:09 -0000 @@ -49,6 +49,7 @@ NONE:INT,INT NONE:NONE NONE:STRING,INT,POINTER +NONE:STRING,POINTER STRING:DOUBLE VOID:DOUBLE VOID:BOOLEAN Index: gtk/gtkwindow.c =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v retrieving revision 1.277 diff -u -r1.277 gtkwindow.c --- gtk/gtkwindow.c 12 Nov 2004 17:51:58 -0000 1.277 +++ gtk/gtkwindow.c 18 Nov 2004 15:58:10 -0000 @@ -26,8 +26,12 @@ #include #include +#include #include #include "gdk/gdk.h" +#ifdef GDK_WINDOWING_X11 +#include "x11/gdkx.h" +#endif #include "gdk/gdkkeysyms.h" #include "gtkalias.h" @@ -51,6 +55,7 @@ ACTIVATE_DEFAULT, MOVE_FOCUS, KEYS_CHANGED, + CHANGE_SCREEN, LAST_SIGNAL }; @@ -169,6 +174,7 @@ guint skips_pager : 1; guint accept_focus : 1; guint focus_on_map : 1; + guint allow_screen_change : 1; }; static void gtk_window_class_init (GtkWindowClass *klass); @@ -219,6 +225,9 @@ static void gtk_window_keys_changed (GtkWindow *window); static void gtk_window_read_rcfiles (GtkWidget *widget, GdkEventClient *event); +static void gtk_window_change_screen (GtkWindow *window, + const gchar *display_name, + GtkWindowChangeScreenStatus *status); static void gtk_window_paint (GtkWidget *widget, GdkRectangle *area); static gint gtk_window_expose (GtkWidget *widget, @@ -445,6 +454,7 @@ klass->activate_focus = gtk_window_real_activate_focus; klass->move_focus = gtk_window_move_focus; klass->keys_changed = gtk_window_keys_changed; + klass->change_screen = gtk_window_change_screen; g_type_class_add_private (gobject_class, sizeof (GtkWindowPrivate)); @@ -740,6 +750,16 @@ G_TYPE_NONE, 0); + window_signals[CHANGE_SCREEN] = + g_signal_new ("change_screen", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkWindowClass, change_screen), + NULL, NULL, + _gtk_marshal_VOID__STRING_POINTER, + G_TYPE_NONE, + 2, G_TYPE_STRING, G_TYPE_POINTER); + /* * Key bindings */ @@ -4737,16 +4757,121 @@ gtk_rc_reparse_all_for_settings (gtk_widget_get_settings (widget), FALSE); } +#ifdef GDK_WINDOWING_X11 +static void +gtk_window_handle_change_display (GtkWidget *window, + GdkEventClient *event) +{ + GdkDisplay *display; + Atom xprop; + GdkAtom property, type; + gint format, length; + gchar *display_name; + gulong status_window; + guchar *data; + + display = gtk_widget_get_display (window); + xprop = (Atom)event->data.l[2]; + property = gdk_x11_xatom_to_atom_for_display (display, xprop); + status_window = event->data.l[3]; + + display_name = NULL; + + if (gdk_property_get (window->window, + property, + GDK_NONE, + 0, 4096, 0, + &type, &format, &length, &data)) + { + gchar **list; + gint i; + gint count; + gchar **lines; + + count = gdk_text_property_to_utf8_list_for_display (display, + type, + format, + data, + length, + &list); + if (count > 0) + { + lines = g_strsplit (list[0], "\n", 2); + display_name = g_strdup (lines[0]); + g_strfreev (lines); + } + + for (i = 0; i < count; i++) + g_free (list[i]); + g_free (list); + + g_free (data); + } + +#if DEBUG_CHANGE_SCREEN + g_print ("change display: property %s status window %#x display %s\n", + gdk_atom_name (property), status_window, display_name); +#endif + + if (display_name) + { + GtkWindowChangeScreenStatus status; + GdkEventClient response; + + response.type = GDK_CLIENT_EVENT; + response.window = event->window; + response.data_format = 32; + response.message_type = gdk_atom_intern ("_NET_CHANGE_DISPLAY", FALSE); + response.data.l[0] = GDK_WINDOW_XID (event->window); + + if (gdk_window_get_allow_screen_change (window->window)) + { + status = GTK_WINDOW_CHANGE_SCREEN_PENDING; + + g_signal_emit (window, window_signals[CHANGE_SCREEN], 0, + display_name, &status); + } + else + status = GTK_WINDOW_CHANGE_SCREEN_REFUSED; + + g_assert (status != GTK_WINDOW_CHANGE_SCREEN_PENDING); + + response.data.l[1] = status; + + if (status == GTK_WINDOW_CHANGE_SCREEN_SUCCESS) + response.data.l[2] = GDK_WINDOW_XID (window->window); + else + response.data.l[2] = None; + + gdk_event_send_client_message_for_display (display, + (GdkEvent *)&response, + status_window); + + g_free (display_name); + } +} +#endif + static gint gtk_window_client_event (GtkWidget *widget, GdkEventClient *event) { + GdkDisplay *display; + if (!atom_rcfiles) atom_rcfiles = gdk_atom_intern ("_GTK_READ_RCFILES", FALSE); if (event->message_type == atom_rcfiles) gtk_window_read_rcfiles (widget, event); +#ifdef GDK_WINDOWING_X11 + display = gtk_widget_get_display (widget); + if (event->message_type == gdk_atom_intern ("WM_PROTOCOLS", FALSE) && + (Atom)event->data.l[0] == gdk_x11_get_xatom_by_name_for_display (display, + "_NET_CHANGE_DISPLAY")) + gtk_window_handle_change_display (widget, event); +#endif + return FALSE; } @@ -7626,4 +7751,121 @@ gtk_window_set_auto_startup_notification (gboolean setting) { disable_startup_notification = !setting; +} + +void +gtk_window_set_allow_screen_change (GtkWindow *window, + gboolean allow) +{ + GtkWindowPrivate *priv; + GdkWindow *toplevel; + + g_return_if_fail (GTK_IS_WINDOW (window)); + + priv = GTK_WINDOW_GET_PRIVATE (window); + + allow = allow != FALSE; + + if (priv->allow_screen_change != allow) + { + priv->allow_screen_change = allow; + + if (window->frame) + toplevel = window->frame; + else + toplevel = GTK_WIDGET (window)->window; + + if (toplevel != NULL) + gdk_window_set_allow_screen_change (toplevel, allow); + + } +} + +static void +gtk_window_change_screen (GtkWindow *window, + const gchar *display_name, + GtkWindowChangeScreenStatus *status) +{ + GdkDisplayManager *dm; + GdkDisplay *display; + GdkScreen *screen; + GSList *displays, *d; + gint screen_num, dot_pos; + gchar *dot; + + if (*status != GTK_WINDOW_CHANGE_SCREEN_PENDING) + return; + + dot = strrchr (display_name, '.'); + if (dot) + { + screen_num = strtol (dot + 1, NULL, 10); + dot_pos = dot - display_name; + } + else + { + screen_num = -1; + dot_pos = strlen (display_name); + } + + display = NULL; + dm = gdk_display_manager_get (); + displays = gdk_display_manager_list_displays (dm); + +#if DEBUG_CHANGE_SCREEN + { + gchar *part = g_strndup (display_name, dot_pos + 1); + g_print ("looking for display, got %s, stripped to %s, %d displays open\n", + display_name, part, g_slist_length (displays)); + g_free (part); + } +#endif + + for (d = displays; d; d = d->next) + { + if (strncmp (display_name, + gdk_display_get_name (GDK_DISPLAY_OBJECT (d->data)), + dot_pos + 1) == 0) + { + display = GDK_DISPLAY_OBJECT (d->data); + break; + } + } + g_slist_free (displays); + + if (!display) + { +#if DEBUG_CHANGE_SCREEN + g_print ("trying to open %s\n", display_name); +#endif + display = gdk_display_open (display_name); + } + + if (!display) + { +#if DEBUG_CHANGE_SCREEN + g_print ("no display\n"); +#endif + + *status = GTK_WINDOW_CHANGE_SCREEN_NO_DISPLAY; + return; + } + + if (screen_num == -1) + screen = gdk_display_get_default_screen (display); + else + screen = gdk_display_get_screen (display, screen_num); + + if (!screen) + { +#if 1 + g_print ("screen %d not found\n", screen_num); +#endif + *status = GTK_WINDOW_CHANGE_SCREEN_NO_SCREEN; + return; + } + + gtk_window_set_screen (window, screen); + + *status = GTK_WINDOW_CHANGE_SCREEN_SUCCESS; } Index: gtk/gtkwindow.h =================================================================== RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.h,v retrieving revision 1.77 diff -u -r1.77 gtkwindow.h --- gtk/gtkwindow.h 17 Jul 2004 03:55:07 -0000 1.77 +++ gtk/gtkwindow.h 18 Nov 2004 15:58:10 -0000 @@ -114,6 +114,16 @@ GdkScreen *screen; }; +typedef enum { + GTK_WINDOW_CHANGE_SCREEN_PENDING = -1, + GTK_WINDOW_CHANGE_SCREEN_SUCCESS = 0, + GTK_WINDOW_CHANGE_SCREEN_NO_DISPLAY = 1, + GTK_WINDOW_CHANGE_SCREEN_NO_SCREEN = 2, + GTK_WINDOW_CHANGE_SCREEN_NO_AUTH = 3, + GTK_WINDOW_CHANGE_SCREEN_FAILURE = 4, + GTK_WINDOW_CHANGE_SCREEN_REFUSED = 5 +} GtkWindowChangeScreenStatus; + struct _GtkWindowClass { GtkBinClass parent_class; @@ -131,12 +141,14 @@ GtkDirectionType direction); void (*keys_changed) (GtkWindow *window); + void (*change_screen) (GtkWindow *window, + const gchar *display_name, + GtkWindowChangeScreenStatus *status); /* Padding for future expansion */ void (*_gtk_reserved1) (void); void (*_gtk_reserved2) (void); void (*_gtk_reserved3) (void); - void (*_gtk_reserved4) (void); }; #define GTK_TYPE_WINDOW_GROUP (gtk_window_group_get_type ()) @@ -309,6 +321,8 @@ void gtk_window_unfullscreen (GtkWindow *window); void gtk_window_set_keep_above (GtkWindow *window, gboolean setting); void gtk_window_set_keep_below (GtkWindow *window, gboolean setting); +void gtk_window_set_allow_screen_change (GtkWindow *window, gboolean allow); +gboolean gtk_window_get_allow_screen_change (GtkWindow *window); void gtk_window_begin_resize_drag (GtkWindow *window, GdkWindowEdge edge,