466 lines
12 KiB
C
466 lines
12 KiB
C
/* Clutter.
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* Copyright (C) 2006, 2007, 2008 OpenedHand Ltd
|
|
* Copyright (C) 2009, 2010 Intel Corp.
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*
|
|
*
|
|
* Authored by:
|
|
* Matthew Allum <mallum@openedhand.com>
|
|
* Emmanuele Bassi <ebassi@linux.intel.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "clutter-backend-x11.h"
|
|
#include "clutter-x11.h"
|
|
|
|
#include "clutter-backend-private.h"
|
|
#include "clutter-debug.h"
|
|
#include "clutter-event-private.h"
|
|
#include "clutter-main.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#if 0
|
|
/* XEMBED protocol support for toolkit embedding */
|
|
#define XEMBED_MAPPED (1 << 0)
|
|
#define MAX_SUPPORTED_XEMBED_VERSION 1
|
|
|
|
#define XEMBED_EMBEDDED_NOTIFY 0
|
|
#define XEMBED_WINDOW_ACTIVATE 1
|
|
#define XEMBED_WINDOW_DEACTIVATE 2
|
|
#define XEMBED_REQUEST_FOCUS 3
|
|
#define XEMBED_FOCUS_IN 4
|
|
#define XEMBED_FOCUS_OUT 5
|
|
#define XEMBED_FOCUS_NEXT 6
|
|
#define XEMBED_FOCUS_PREV 7
|
|
/* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */
|
|
#define XEMBED_MODALITY_ON 10
|
|
#define XEMBED_MODALITY_OFF 11
|
|
#define XEMBED_REGISTER_ACCELERATOR 12
|
|
#define XEMBED_UNREGISTER_ACCELERATOR 13
|
|
#define XEMBED_ACTIVATE_ACCELERATOR 14
|
|
|
|
static Window ParentEmbedderWin = None;
|
|
#endif
|
|
|
|
typedef struct _ClutterEventSource ClutterEventSource;
|
|
|
|
struct _ClutterEventSource
|
|
{
|
|
GSource source;
|
|
|
|
ClutterBackend *backend;
|
|
GPollFD event_poll_fd;
|
|
};
|
|
|
|
ClutterEventX11 *
|
|
_clutter_event_x11_new (void)
|
|
{
|
|
return g_slice_new0 (ClutterEventX11);
|
|
}
|
|
|
|
ClutterEventX11 *
|
|
_clutter_event_x11_copy (ClutterEventX11 *event_x11)
|
|
{
|
|
if (event_x11 != NULL)
|
|
return g_slice_dup (ClutterEventX11, event_x11);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
_clutter_event_x11_free (ClutterEventX11 *event_x11)
|
|
{
|
|
if (event_x11 != NULL)
|
|
g_slice_free (ClutterEventX11, event_x11);
|
|
}
|
|
|
|
static gboolean clutter_event_prepare (GSource *source,
|
|
gint *timeout);
|
|
static gboolean clutter_event_check (GSource *source);
|
|
static gboolean clutter_event_dispatch (GSource *source,
|
|
GSourceFunc callback,
|
|
gpointer user_data);
|
|
|
|
static GList *event_sources = NULL;
|
|
|
|
static GSourceFuncs event_funcs = {
|
|
clutter_event_prepare,
|
|
clutter_event_check,
|
|
clutter_event_dispatch,
|
|
NULL
|
|
};
|
|
|
|
static GSource *
|
|
clutter_event_source_new (ClutterBackend *backend)
|
|
{
|
|
GSource *source = g_source_new (&event_funcs, sizeof (ClutterEventSource));
|
|
ClutterEventSource *event_source = (ClutterEventSource *) source;
|
|
|
|
event_source->backend = backend;
|
|
|
|
return source;
|
|
}
|
|
|
|
static gboolean
|
|
check_xpending (ClutterBackend *backend)
|
|
{
|
|
return XPending (CLUTTER_BACKEND_X11 (backend)->xdpy);
|
|
}
|
|
|
|
#if 0
|
|
static gboolean
|
|
xembed_send_message (ClutterBackendX11 *backend_x11,
|
|
Window window,
|
|
long message,
|
|
long detail,
|
|
long data1,
|
|
long data2)
|
|
{
|
|
XEvent ev;
|
|
|
|
memset (&ev, 0, sizeof (ev));
|
|
|
|
ev.xclient.type = ClientMessage;
|
|
ev.xclient.window = window;
|
|
ev.xclient.message_type = backend_x11->atom_XEMBED;
|
|
ev.xclient.format = 32;
|
|
ev.xclient.data.l[0] = CurrentTime;
|
|
ev.xclient.data.l[1] = message;
|
|
ev.xclient.data.l[2] = detail;
|
|
ev.xclient.data.l[3] = data1;
|
|
ev.xclient.data.l[4] = data2;
|
|
|
|
clutter_x11_trap_x_errors ();
|
|
|
|
XSendEvent (backend_x11->xdpy, window, False, NoEventMask, &ev);
|
|
XSync (backend_x11->xdpy, False);
|
|
|
|
if (clutter_x11_untrap_x_errors ())
|
|
return False;
|
|
|
|
return True;
|
|
}
|
|
|
|
static void
|
|
xembed_set_info (ClutterBackendX11 *backend_x11,
|
|
Window window,
|
|
gint flags)
|
|
{
|
|
gint32 list[2];
|
|
|
|
list[0] = MAX_SUPPORTED_XEMBED_VERSION;
|
|
list[1] = XEMBED_MAPPED;
|
|
|
|
XChangeProperty (backend_x11->xdpy, window,
|
|
backend_x11->atom_XEMBED_INFO,
|
|
backend_x11->atom_XEMBED_INFO, 32,
|
|
PropModeReplace, (unsigned char *) list, 2);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
_clutter_backend_x11_events_init (ClutterBackend *backend)
|
|
{
|
|
ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
|
|
GSource *source;
|
|
ClutterEventSource *event_source;
|
|
int connection_number;
|
|
gchar *name;
|
|
|
|
connection_number = ConnectionNumber (backend_x11->xdpy);
|
|
CLUTTER_NOTE (EVENT, "Connection number: %d", connection_number);
|
|
|
|
source = backend_x11->event_source = clutter_event_source_new (backend);
|
|
event_source = (ClutterEventSource *) source;
|
|
g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS);
|
|
|
|
name = g_strdup_printf ("Clutter X11 Event (connection: %d)",
|
|
connection_number);
|
|
g_source_set_name (source, name);
|
|
g_free (name);
|
|
|
|
event_source->event_poll_fd.fd = connection_number;
|
|
event_source->event_poll_fd.events = G_IO_IN;
|
|
|
|
event_sources = g_list_prepend (event_sources, event_source);
|
|
|
|
g_source_add_poll (source, &event_source->event_poll_fd);
|
|
g_source_set_can_recurse (source, TRUE);
|
|
g_source_attach (source, NULL);
|
|
}
|
|
|
|
void
|
|
_clutter_backend_x11_events_uninit (ClutterBackend *backend)
|
|
{
|
|
ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
|
|
|
|
if (backend_x11->event_source)
|
|
{
|
|
CLUTTER_NOTE (EVENT, "Destroying the event source");
|
|
|
|
event_sources = g_list_remove (event_sources,
|
|
backend_x11->event_source);
|
|
|
|
g_source_destroy (backend_x11->event_source);
|
|
g_source_unref (backend_x11->event_source);
|
|
backend_x11->event_source = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
events_queue (ClutterBackend *backend)
|
|
{
|
|
ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
|
|
Display *xdisplay = backend_x11->xdpy;
|
|
ClutterEvent *event;
|
|
XEvent xevent;
|
|
|
|
while (!clutter_events_pending () && XPending (xdisplay))
|
|
{
|
|
XNextEvent (xdisplay, &xevent);
|
|
|
|
event = clutter_event_new (CLUTTER_NOTHING);
|
|
|
|
#ifdef HAVE_XGE
|
|
XGetEventData (xdisplay, &xevent.xcookie);
|
|
#endif
|
|
|
|
if (_clutter_backend_translate_event (backend, &xevent, event))
|
|
_clutter_event_push (event, FALSE);
|
|
else
|
|
clutter_event_free (event);
|
|
|
|
#ifdef HAVE_XGE
|
|
XFreeEventData (xdisplay, &xevent.xcookie);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_x11_handle_event:
|
|
* @xevent: pointer to XEvent structure
|
|
*
|
|
* This function processes a single X event; it can be used to hook
|
|
* into external X11 event processing (for example, a GDK filter
|
|
* function).
|
|
*
|
|
* If clutter_x11_disable_event_retrieval() has been called, you must
|
|
* let this function process events to update Clutter's internal state.
|
|
*
|
|
* Return value: #ClutterX11FilterReturn. %CLUTTER_X11_FILTER_REMOVE
|
|
* indicates that Clutter has internally handled the event and the
|
|
* caller should do no further processing. %CLUTTER_X11_FILTER_CONTINUE
|
|
* indicates that Clutter is either not interested in the event,
|
|
* or has used the event to update internal state without taking
|
|
* any exclusive action. %CLUTTER_X11_FILTER_TRANSLATE will not
|
|
* occur.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
ClutterX11FilterReturn
|
|
clutter_x11_handle_event (XEvent *xevent)
|
|
{
|
|
ClutterX11FilterReturn result;
|
|
ClutterBackend *backend;
|
|
ClutterEvent *event;
|
|
gint spin = 1;
|
|
#ifdef HAVE_XGE
|
|
ClutterBackendX11 *backend_x11;
|
|
Display *xdisplay;
|
|
gboolean allocated_event;
|
|
#endif
|
|
|
|
/* The return values here are someone approximate; we return
|
|
* CLUTTER_X11_FILTER_REMOVE if a clutter event is
|
|
* generated for the event. This mostly, but not entirely,
|
|
* corresponds to whether other event processing should be
|
|
* excluded. As long as the stage window is not shared with another
|
|
* toolkit it should be safe, and never return
|
|
* %CLUTTER_X11_FILTER_REMOVE when more processing is needed.
|
|
*/
|
|
|
|
result = CLUTTER_X11_FILTER_CONTINUE;
|
|
|
|
clutter_threads_enter ();
|
|
|
|
backend = clutter_get_default_backend ();
|
|
|
|
event = clutter_event_new (CLUTTER_NOTHING);
|
|
|
|
#ifdef HAVE_XGE
|
|
backend_x11 = CLUTTER_BACKEND_X11 (backend);
|
|
xdisplay = backend_x11->xdpy;
|
|
|
|
allocated_event = XGetEventData (xdisplay, &xevent->xcookie);
|
|
#endif
|
|
|
|
if (_clutter_backend_translate_event (backend, xevent, event))
|
|
{
|
|
_clutter_event_push (event, FALSE);
|
|
|
|
result = CLUTTER_X11_FILTER_REMOVE;
|
|
}
|
|
else
|
|
{
|
|
clutter_event_free (event);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Motion events can generate synthetic enter and leave events, so if we
|
|
* are processing a motion event, we need to spin the event loop at least
|
|
* two extra times to pump the enter/leave events through (otherwise they
|
|
* just get pushed down the queue and never processed).
|
|
*/
|
|
if (event->type == CLUTTER_MOTION)
|
|
spin += 2;
|
|
|
|
while (spin > 0 && (event = clutter_event_get ()))
|
|
{
|
|
/* forward the event into clutter for emission etc. */
|
|
clutter_do_event (event);
|
|
clutter_event_free (event);
|
|
--spin;
|
|
}
|
|
|
|
out:
|
|
#ifdef HAVE_XGE
|
|
if (allocated_event)
|
|
XFreeEventData (xdisplay, &xevent->xcookie);
|
|
#endif
|
|
|
|
clutter_threads_leave ();
|
|
|
|
return result;
|
|
}
|
|
|
|
static gboolean
|
|
clutter_event_prepare (GSource *source,
|
|
gint *timeout)
|
|
{
|
|
ClutterBackend *backend = ((ClutterEventSource *) source)->backend;
|
|
gboolean retval;
|
|
|
|
clutter_threads_enter ();
|
|
|
|
*timeout = -1;
|
|
retval = (clutter_events_pending () || check_xpending (backend));
|
|
|
|
clutter_threads_leave ();
|
|
|
|
return retval;
|
|
}
|
|
|
|
static gboolean
|
|
clutter_event_check (GSource *source)
|
|
{
|
|
ClutterEventSource *event_source = (ClutterEventSource *) source;
|
|
ClutterBackend *backend = event_source->backend;
|
|
gboolean retval;
|
|
|
|
clutter_threads_enter ();
|
|
|
|
if (event_source->event_poll_fd.revents & G_IO_IN)
|
|
retval = (clutter_events_pending () || check_xpending (backend));
|
|
else
|
|
retval = FALSE;
|
|
|
|
clutter_threads_leave ();
|
|
|
|
return retval;
|
|
}
|
|
|
|
static gboolean
|
|
clutter_event_dispatch (GSource *source,
|
|
GSourceFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
ClutterBackend *backend = ((ClutterEventSource *) source)->backend;
|
|
ClutterEvent *event;
|
|
|
|
clutter_threads_enter ();
|
|
|
|
/* Grab the event(s), translate and figure out double click.
|
|
* The push onto queue (stack) if valid.
|
|
*/
|
|
events_queue (backend);
|
|
|
|
/* Pop an event off the queue if any */
|
|
event = clutter_event_get ();
|
|
if (event != NULL)
|
|
{
|
|
/* forward the event into clutter for emission etc. */
|
|
clutter_do_event (event);
|
|
clutter_event_free (event);
|
|
}
|
|
|
|
clutter_threads_leave ();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* clutter_x11_get_current_event_time: (skip)
|
|
*
|
|
* Retrieves the timestamp of the last X11 event processed by
|
|
* Clutter. This might be different from the timestamp returned
|
|
* by clutter_get_current_event_time(), as Clutter may synthesize
|
|
* or throttle events.
|
|
*
|
|
* Return value: a timestamp, in milliseconds
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
Time
|
|
clutter_x11_get_current_event_time (void)
|
|
{
|
|
ClutterBackend *backend = clutter_get_default_backend ();
|
|
|
|
return CLUTTER_BACKEND_X11 (backend)->last_event_time;
|
|
}
|
|
|
|
/**
|
|
* clutter_x11_event_get_key_group:
|
|
* @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS or %CLUTTER_KEY_RELEASE
|
|
*
|
|
* Retrieves the group for the modifiers set in @event
|
|
*
|
|
* Return value: the group id
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
gint
|
|
clutter_x11_event_get_key_group (const ClutterEvent *event)
|
|
{
|
|
ClutterEventX11 *event_x11;
|
|
|
|
g_return_val_if_fail (event != NULL, 0);
|
|
g_return_val_if_fail (event->type == CLUTTER_KEY_PRESS ||
|
|
event->type == CLUTTER_KEY_RELEASE, 0);
|
|
|
|
event_x11 = _clutter_event_get_platform_data (event);
|
|
if (event_x11 == NULL)
|
|
return 0;
|
|
|
|
return event_x11->key_group;
|
|
}
|