Add support for WM_PROTOCOLS to the ClutterStage window

Supporting WM_PROTOCOLS allows handling the WM_DELETE_WINDOW ClientMessage,
which is then relayed to the ClutterStage actor in form of the "delete-event"
signal. Returning TRUE will block the propagation and won't close the
stage window.
This commit is contained in:
Emmanuele Bassi 2007-04-19 15:26:54 +00:00
parent f1a1399927
commit b2d01d86c2
6 changed files with 94 additions and 29 deletions

View File

@ -58,7 +58,8 @@ typedef enum
CLUTTER_SCROLL, CLUTTER_SCROLL,
CLUTTER_STAGE_STATE, CLUTTER_STAGE_STATE,
CLUTTER_DESTROY_NOTIFY, CLUTTER_DESTROY_NOTIFY,
CLUTTER_CLIENT_MESSAGE CLUTTER_CLIENT_MESSAGE,
CLUTTER_DELETE
} ClutterEventType; } ClutterEventType;
typedef enum typedef enum
@ -80,6 +81,7 @@ typedef enum
typedef enum typedef enum
{ {
CLUTTER_FILTER_CONTINUE, CLUTTER_FILTER_CONTINUE,
CLUTTER_FILTER_TRANSLATE,
CLUTTER_FILTER_REMOVE CLUTTER_FILTER_REMOVE
} ClutterFilterResponse; } ClutterFilterResponse;

View File

@ -153,11 +153,23 @@ clutter_main_do_event (ClutterEvent *event,
case CLUTTER_MOTION: case CLUTTER_MOTION:
g_signal_emit_by_name (stage, "motion-event", event); g_signal_emit_by_name (stage, "motion-event", event);
break; break;
case CLUTTER_DESTROY_NOTIFY: case CLUTTER_DELETE:
g_signal_emit_by_name (stage, "delete-event"); {
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; break;
case CLUTTER_STAGE_STATE: case CLUTTER_STAGE_STATE:
break; break;
case CLUTTER_DESTROY_NOTIFY:
break;
case CLUTTER_CLIENT_MESSAGE:
break;
} }
} }

View File

@ -92,19 +92,6 @@ enum
static guint stage_signals[LAST_SIGNAL] = { 0 }; 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 static void
clutter_stage_paint (ClutterActor *actor) clutter_stage_paint (ClutterActor *actor)
{ {
@ -208,8 +195,6 @@ clutter_stage_class_init (ClutterStageClass *klass)
actor_class->paint = clutter_stage_paint; actor_class->paint = clutter_stage_paint;
stage_class->delete_event = clutter_stage_delete_event;
/** /**
* ClutterStage:fullscreen * ClutterStage:fullscreen
* *

View File

@ -60,7 +60,9 @@
#define XEMBED_UNREGISTER_ACCELERATOR 13 #define XEMBED_UNREGISTER_ACCELERATOR 13
#define XEMBED_ACTIVATE_ACCELERATOR 14 #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; static Window ParentEmbedderWin = None;
typedef struct _ClutterEventSource ClutterEventSource; typedef struct _ClutterEventSource ClutterEventSource;
@ -172,6 +174,7 @@ _clutter_events_init (ClutterBackend *backend)
CLUTTER_NOTE (EVENT, "Connection number: %d", connection_number); CLUTTER_NOTE (EVENT, "Connection number: %d", connection_number);
Atom_XEMBED = XInternAtom (backend_glx->xdpy, "_XEMBED", False); 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); source = backend_glx->event_source = clutter_event_source_new (backend);
event_source = (ClutterEventSource *) source; event_source = (ClutterEventSource *) source;
@ -211,14 +214,13 @@ _clutter_events_uninit (ClutterBackend *backend)
static void static void
set_user_time (Display *display, set_user_time (Display *display,
Window *xwindow, Window *xwindow,
ClutterEvent *event) long timestamp)
{ {
if (clutter_event_get_time (event) != CLUTTER_CURRENT_TIME) if (timestamp != CLUTTER_CURRENT_TIME)
{ {
Atom atom_WM_USER_TIME; Atom atom_WM_USER_TIME;
long timestamp = clutter_event_get_time (event);
atom_WM_USER_TIME = XInternAtom (display, "_NET_WM_USER_TIME", False); 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 */ 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, handle_xembed_event (ClutterBackendGlx *backend_glx,
XEvent *xevent) XEvent *xevent)
{ {
@ -322,6 +360,9 @@ handle_xembed_event (ClutterBackendGlx *backend_glx,
CLUTTER_NOTE (EVENT, "got unknown XEMBED message"); CLUTTER_NOTE (EVENT, "got unknown XEMBED message");
break; break;
} }
/* do not propagate the XEMBED events to the stage */
return FALSE;
} }
static gboolean static gboolean
@ -366,7 +407,7 @@ clutter_event_translate (ClutterBackend *backend,
case KeyPress: case KeyPress:
event->type = CLUTTER_KEY_PRESS; event->type = CLUTTER_KEY_PRESS;
translate_key_event (backend, event, xevent); 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; break;
case KeyRelease: case KeyRelease:
event->type = CLUTTER_KEY_RELEASE; event->type = CLUTTER_KEY_RELEASE;
@ -408,7 +449,7 @@ clutter_event_translate (ClutterBackend *backend,
break; break;
} }
set_user_time (backend_glx->xdpy, &xwindow, event); set_user_time (backend_glx->xdpy, &xwindow, event->button.time);
break; break;
case ButtonRelease: case ButtonRelease:
/* scroll events don't have a corresponding release */ /* scroll events don't have a corresponding release */
@ -442,9 +483,16 @@ clutter_event_translate (ClutterBackend *backend,
break; break;
case ClientMessage: case ClientMessage:
CLUTTER_NOTE (EVENT, "client message"); 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; 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; break;
default: default:
/* ignore every other event */ /* ignore every other event */

View File

@ -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 static void
clutter_stage_glx_realize (ClutterActor *actor) clutter_stage_glx_realize (ClutterActor *actor)
{ {
@ -168,6 +182,8 @@ clutter_stage_glx_realize (ClutterActor *actor)
ButtonPressMask | ButtonReleaseMask | ButtonPressMask | ButtonReleaseMask |
PropertyChangeMask); PropertyChangeMask);
set_wm_protocols (stage_glx->xdpy, stage_glx->xwin);
if (stage_glx->gl_context) if (stage_glx->gl_context)
glXDestroyContext (stage_glx->xdpy, stage_glx->gl_context); glXDestroyContext (stage_glx->xdpy, stage_glx->gl_context);

View File

@ -47,6 +47,7 @@ Windowing events handled by Clutter.
</para> </para>
@CLUTTER_FILTER_CONTINUE: @CLUTTER_FILTER_CONTINUE:
@CLUTTER_FILTER_TRANSLATE:
@CLUTTER_FILTER_REMOVE: @CLUTTER_FILTER_REMOVE:
<!-- ##### ENUM ClutterScrollDirection ##### --> <!-- ##### ENUM ClutterScrollDirection ##### -->
@ -86,6 +87,7 @@ Windowing events handled by Clutter.
@CLUTTER_STAGE_STATE: @CLUTTER_STAGE_STATE:
@CLUTTER_DESTROY_NOTIFY: @CLUTTER_DESTROY_NOTIFY:
@CLUTTER_CLIENT_MESSAGE: @CLUTTER_CLIENT_MESSAGE:
@CLUTTER_DELETE:
<!-- ##### UNION ClutterEvent ##### --> <!-- ##### UNION ClutterEvent ##### -->
<para> <para>