diff --git a/clutter/clutter-event.h b/clutter/clutter-event.h index 0506fa0fa..5282f9f3f 100644 --- a/clutter/clutter-event.h +++ b/clutter/clutter-event.h @@ -58,7 +58,8 @@ typedef enum CLUTTER_SCROLL, CLUTTER_STAGE_STATE, CLUTTER_DESTROY_NOTIFY, - CLUTTER_CLIENT_MESSAGE + CLUTTER_CLIENT_MESSAGE, + CLUTTER_DELETE } ClutterEventType; typedef enum @@ -80,6 +81,7 @@ typedef enum typedef enum { CLUTTER_FILTER_CONTINUE, + CLUTTER_FILTER_TRANSLATE, CLUTTER_FILTER_REMOVE } ClutterFilterResponse; diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 2d57f7af0..d3d9bc148 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -153,11 +153,23 @@ clutter_main_do_event (ClutterEvent *event, case CLUTTER_MOTION: g_signal_emit_by_name (stage, "motion-event", event); break; - case CLUTTER_DESTROY_NOTIFY: - g_signal_emit_by_name (stage, "delete-event"); + case CLUTTER_DELETE: + { + gboolean res = FALSE; + + g_object_ref (stage); + g_signal_emit_by_name (stage, "delete-event", event, &res); + if (!res) + clutter_main_quit (); + g_object_unref (stage); + } break; case CLUTTER_STAGE_STATE: break; + case CLUTTER_DESTROY_NOTIFY: + break; + case CLUTTER_CLIENT_MESSAGE: + break; } } diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 13d6c82f0..62e20c5f9 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -92,19 +92,6 @@ enum static guint stage_signals[LAST_SIGNAL] = { 0 }; -static gboolean -clutter_stage_delete_event (ClutterStage *stage, - ClutterAnyEvent *event) -{ - /* FIXME - destroy the main stage, probably attaching a weak ref - * to it from the backend, so that it gets destroyed too. - */ - CLUTTER_NOTE (EVENT, "Received a destroy notification"); - - /* we don't want to block */ - return FALSE; -} - static void clutter_stage_paint (ClutterActor *actor) { @@ -208,8 +195,6 @@ clutter_stage_class_init (ClutterStageClass *klass) actor_class->paint = clutter_stage_paint; - stage_class->delete_event = clutter_stage_delete_event; - /** * ClutterStage:fullscreen * diff --git a/clutter/glx/clutter-event-glx.c b/clutter/glx/clutter-event-glx.c index 237e8ca5b..59ef931fb 100644 --- a/clutter/glx/clutter-event-glx.c +++ b/clutter/glx/clutter-event-glx.c @@ -60,7 +60,9 @@ #define XEMBED_UNREGISTER_ACCELERATOR 13 #define XEMBED_ACTIVATE_ACCELERATOR 14 -static Atom Atom_XEMBED = 0; +static Atom Atom_XEMBED = 0; +static Atom Atom_WM_PROTOCOLS = 0; + static Window ParentEmbedderWin = None; typedef struct _ClutterEventSource ClutterEventSource; @@ -172,6 +174,7 @@ _clutter_events_init (ClutterBackend *backend) CLUTTER_NOTE (EVENT, "Connection number: %d", connection_number); Atom_XEMBED = XInternAtom (backend_glx->xdpy, "_XEMBED", False); + Atom_WM_PROTOCOLS = XInternAtom (backend_glx->xdpy, "WM_PROTOCOLS", False); source = backend_glx->event_source = clutter_event_source_new (backend); event_source = (ClutterEventSource *) source; @@ -211,14 +214,13 @@ _clutter_events_uninit (ClutterBackend *backend) static void -set_user_time (Display *display, - Window *xwindow, - ClutterEvent *event) +set_user_time (Display *display, + Window *xwindow, + long timestamp) { - if (clutter_event_get_time (event) != CLUTTER_CURRENT_TIME) + if (timestamp != CLUTTER_CURRENT_TIME) { Atom atom_WM_USER_TIME; - long timestamp = clutter_event_get_time (event); atom_WM_USER_TIME = XInternAtom (display, "_NET_WM_USER_TIME", False); @@ -282,7 +284,43 @@ translate_key_event (ClutterBackend *backend, 0); /* FIXME: index with modifiers */ } -static void +static gboolean +handle_wm_protocols_event (ClutterBackendGlx *backend_glx, + XEvent *xevent) +{ + Atom atom = (Atom) xevent->xclient.data.l[0]; + Atom Atom_WM_DELETE_WINDOW; + + ClutterStage *stage = CLUTTER_STAGE (backend_glx->stage); + Window stage_xwindow = clutter_glx_get_stage_window (stage); + + Atom_WM_DELETE_WINDOW = XInternAtom (backend_glx->xdpy, + "WM_DELETE_WINDOW", + False); + + if (atom == Atom_WM_DELETE_WINDOW && + xevent->xany.window == stage_xwindow) + { + /* the WM_DELETE_WINDOW is a request: we do not destroy + * the window right away, as it might contain vital data; + * we relay the event to the application and we let it + * handle the request + */ + CLUTTER_NOTE (EVENT, "delete window:\twindow: %ld", + xevent->xclient.window); + + set_user_time (backend_glx->xdpy, + &stage_xwindow, + xevent->xclient.data.l[1]); + + return TRUE; + } + + /* do not send the WM_PROTOCOLS events to the queue */ + return FALSE; +} + +static gboolean handle_xembed_event (ClutterBackendGlx *backend_glx, XEvent *xevent) { @@ -322,6 +360,9 @@ handle_xembed_event (ClutterBackendGlx *backend_glx, CLUTTER_NOTE (EVENT, "got unknown XEMBED message"); break; } + + /* do not propagate the XEMBED events to the stage */ + return FALSE; } static gboolean @@ -366,7 +407,7 @@ clutter_event_translate (ClutterBackend *backend, case KeyPress: event->type = CLUTTER_KEY_PRESS; translate_key_event (backend, event, xevent); - set_user_time (backend_glx->xdpy, &xwindow, event); + set_user_time (backend_glx->xdpy, &xwindow, xevent->xkey.time); break; case KeyRelease: event->type = CLUTTER_KEY_RELEASE; @@ -408,7 +449,7 @@ clutter_event_translate (ClutterBackend *backend, break; } - set_user_time (backend_glx->xdpy, &xwindow, event); + set_user_time (backend_glx->xdpy, &xwindow, event->button.time); break; case ButtonRelease: /* scroll events don't have a corresponding release */ @@ -442,9 +483,16 @@ clutter_event_translate (ClutterBackend *backend, break; case ClientMessage: CLUTTER_NOTE (EVENT, "client message"); - if (xevent->xclient.message_type == Atom_XEMBED) - handle_xembed_event (backend_glx, xevent); + event->type = event->any.type = CLUTTER_CLIENT_MESSAGE; + + if (xevent->xclient.message_type == Atom_XEMBED) + res = handle_xembed_event (backend_glx, xevent); + else if (xevent->xclient.message_type == Atom_WM_PROTOCOLS) + { + res = handle_wm_protocols_event (backend_glx, xevent); + event->type = event->any.type = CLUTTER_DELETE; + } break; default: /* ignore every other event */ diff --git a/clutter/glx/clutter-stage-glx.c b/clutter/glx/clutter-stage-glx.c index 9cf2e3247..c5109c4d9 100644 --- a/clutter/glx/clutter-stage-glx.c +++ b/clutter/glx/clutter-stage-glx.c @@ -108,6 +108,20 @@ clutter_stage_glx_unrealize (ClutterActor *actor) } } +static void +set_wm_protocols (Display *xdisplay, + Window xwindow) +{ + Atom protocols[3]; + int n = 0; + + protocols[n++] = XInternAtom (xdisplay, "WM_DELETE_WINDOW", False); + protocols[n++] = XInternAtom (xdisplay, "WM_TAKE_FOCUS", False); + protocols[n++] = XInternAtom (xdisplay, "_NET_WM_PING", False); + + XSetWMProtocols (xdisplay, xwindow, protocols, n); +} + static void clutter_stage_glx_realize (ClutterActor *actor) { @@ -168,6 +182,8 @@ clutter_stage_glx_realize (ClutterActor *actor) ButtonPressMask | ButtonReleaseMask | PropertyChangeMask); + set_wm_protocols (stage_glx->xdpy, stage_glx->xwin); + if (stage_glx->gl_context) glXDestroyContext (stage_glx->xdpy, stage_glx->gl_context); diff --git a/doc/reference/tmpl/clutter-event.sgml b/doc/reference/tmpl/clutter-event.sgml index 3aa661797..19c7e1d6c 100644 --- a/doc/reference/tmpl/clutter-event.sgml +++ b/doc/reference/tmpl/clutter-event.sgml @@ -47,6 +47,7 @@ Windowing events handled by Clutter. @CLUTTER_FILTER_CONTINUE: +@CLUTTER_FILTER_TRANSLATE: @CLUTTER_FILTER_REMOVE: @@ -86,6 +87,7 @@ Windowing events handled by Clutter. @CLUTTER_STAGE_STATE: @CLUTTER_DESTROY_NOTIFY: @CLUTTER_CLIENT_MESSAGE: +@CLUTTER_DELETE: