osx: Improve the event loop

Implementation of event loop which works with GLib events, native OS X
events and Clutter events.

The event loop source code comes from the equivalent code in the Quartz
GDK backend from GTK+ 2.22.1, which is LGPL v2.1+ and thus compatible
with Clutter's licensing terms.

The code has been tested with libsoup, which did not work before together
with Clutter.

Signed-off-by: Emmanuele Bassi <ebassi@linux.intel.com>

http://bugzilla.clutter-project.org/show_bug.cgi?id=2490
This commit is contained in:
Viatcheslav Gachkaylo 2011-01-28 12:56:57 +00:00 committed by Emmanuele Bassi
parent 1558975bc3
commit 634e4ffd1a
6 changed files with 1176 additions and 142 deletions

View File

@ -500,10 +500,14 @@ osx_source_h = $(srcdir)/osx/clutter-osx.h
osx_source_h_priv = \
$(srcdir)/osx/clutter-backend-osx.h \
$(srcdir)/osx/clutter-event-loop-osx.h \
$(srcdir)/osx/clutter-stage-osx.h \
$(NULL)
osx_source_c_priv = $(srcdir)/osx/clutter-event-osx.c
osx_source_c_priv = \
$(srcdir)/osx/clutter-event-loop-osx.c \
$(srcdir)/osx/clutter-event-osx.c \
$(NULL)
if SUPPORT_OSX
# we need to tell the compiler that part of our code base is

View File

@ -24,7 +24,8 @@ typedef enum {
CLUTTER_DEBUG_MULTISTAGE = 1 << 13,
CLUTTER_DEBUG_ANIMATION = 1 << 14,
CLUTTER_DEBUG_LAYOUT = 1 << 15,
CLUTTER_DEBUG_PICK = 1 << 16
CLUTTER_DEBUG_PICK = 1 << 16,
CLUTTER_DEBUG_EVENTLOOP = 1 << 17
} ClutterDebugFlag;
typedef enum {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
/* Clutter - An OpenGL based 'interactive canvas' library.
* OSX backend - event loop
*
* Copyright (C) 2005-2007 Imendio AB
* Copyright (C) 2011 Crystalnix <vgachkaylo@crystalnix.com>
*
* 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/>.
*
*
*/
#ifndef __CLUTTER_EVENT_LOOP_OSX_H__
#define __CLUTTER_EVENT_LOOP_OSX_H__
G_BEGIN_DECLS
/* Initialization */
void _clutter_osx_event_loop_init (void);
G_END_DECLS
#endif

View File

@ -3,6 +3,7 @@
*
* Copyright (C) 2007-2008 Tommi Komulainen <tommi.komulainen@iki.fi>
* Copyright (C) 2007 OpenedHand Ltd.
* Copyright (C) 2011 Crystalnix <vgachkaylo@crystalnix.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -31,11 +32,9 @@
#include <clutter/clutter-private.h>
#include <clutter/clutter-keysyms.h>
/* Overriding the poll function because the events are not delivered over file
* descriptors and setting up a GSource would just introduce polling.
*/
#include "clutter-event-loop-osx.h"
static GPollFunc old_poll_func = NULL;
#define WHEEL_DELTA 1
/*************************************************************************/
@interface NSEvent (Clutter)
@ -206,9 +205,74 @@ static GPollFunc old_poll_func = NULL;
@end
/*************************************************************************/
static void
take_and_queue_event (ClutterEvent *event)
{
ClutterMainContext *clutter_context;
clutter_context = _clutter_context_get_default ();
/* The event is added directly to the queue instead of using
clutter_event_put so that it can avoid a copy. This takes
ownership of the event */
g_queue_push_head (clutter_context->events_queue, event);
}
static void
process_scroll_event(ClutterEvent *event, gboolean isVertical)
{
ClutterStageWindow *impl;
ClutterStageOSX *stage_osx;
impl = _clutter_stage_get_window (event->any.stage);
stage_osx = CLUTTER_STAGE_OSX (impl);
gfloat *scroll_pos = isVertical ? &(stage_osx->scroll_pos_y) : &(stage_osx->scroll_pos_x);
while (abs (*scroll_pos) >= WHEEL_DELTA)
{
ClutterEvent *event_gen = clutter_event_new (CLUTTER_SCROLL);
event_gen->scroll.time = event->any.time;
event_gen->scroll.modifier_state = event->scroll.modifier_state;
event_gen->any.stage = event->any.stage;
event_gen->scroll.x = event->scroll.x;
event_gen->scroll.y = event->scroll.y;
if (*scroll_pos > 0)
{
event_gen->scroll.direction = isVertical ? CLUTTER_SCROLL_UP : CLUTTER_SCROLL_RIGHT;
*scroll_pos -= WHEEL_DELTA;
}
else
{
event_gen->scroll.direction = isVertical ? CLUTTER_SCROLL_DOWN : CLUTTER_SCROLL_LEFT;
*scroll_pos += WHEEL_DELTA;
}
take_and_queue_event (event_gen);
CLUTTER_NOTE (EVENT, "scroll %s at %f,%f",
(event_gen->scroll.direction == CLUTTER_SCROLL_UP) ? "UP" :
(
(event_gen->scroll.direction == CLUTTER_SCROLL_DOWN) ? "DOWN" :
(
(event_gen->scroll.direction == CLUTTER_SCROLL_RIGHT) ? "RIGHT" : "LEFT")),
(float)event->scroll.x, (float)event->scroll.y);
}
}
static gboolean
clutter_event_osx_translate (NSEvent *nsevent, ClutterEvent *event)
{
{
ClutterStageWindow *impl;
ClutterStageOSX *stage_osx;
impl = _clutter_stage_get_window (event->any.stage);
stage_osx = CLUTTER_STAGE_OSX (impl);
event->any.time = [nsevent clutterTime];
switch ([nsevent type])
@ -250,6 +314,18 @@ clutter_event_osx_translate (NSEvent *nsevent, ClutterEvent *event)
(float)event->button.x, (float)event->button.y);
return TRUE;
case NSScrollWheel:
stage_osx->scroll_pos_x += [nsevent deltaX];
stage_osx->scroll_pos_y += [nsevent deltaY];
[nsevent clutterX:&(event->scroll.x) y:&(event->scroll.y)];
event->scroll.modifier_state = [nsevent clutterModifierState];
process_scroll_event(event, TRUE);
process_scroll_event(event, FALSE);
return FALSE;
case NSKeyDown:
event->type = CLUTTER_KEY_PRESS;
/* fall through */
@ -292,147 +368,14 @@ _clutter_event_osx_put (NSEvent *nsevent, ClutterStage *wrapper)
}
}
typedef struct {
CFSocketRef sock;
CFRunLoopSourceRef source;
gushort revents;
} SocketInfo;
static void
socket_activity_cb (CFSocketRef sock,
CFSocketCallBackType cbtype,
CFDataRef address,
const void *data,
void *info)
{
SocketInfo *si = info;
if (cbtype & kCFSocketReadCallBack)
si->revents |= G_IO_IN;
if (cbtype & kCFSocketWriteCallBack)
si->revents |= G_IO_OUT;
}
static gint
clutter_event_osx_poll_func (GPollFD *ufds, guint nfds, gint timeout)
{
NSDate *until_date;
NSEvent *nsevent;
SocketInfo *sockets = NULL;
gint n_active = 0;
CLUTTER_OSX_POOL_ALLOC();
if (timeout == -1)
until_date = [NSDate distantFuture];
else if (timeout == 0)
until_date = [NSDate distantPast];
else
until_date = [NSDate dateWithTimeIntervalSinceNow:timeout/1000.0];
/* File descriptors appear to be similar enough to sockets so that they can
* be used in CFRunLoopSource.
*
* We could also launch a thread to call old_poll_func and signal the main
* thread. No idea which way is better.
*/
if (nfds > 0)
{
CFRunLoopRef run_loop;
run_loop = [[NSRunLoop currentRunLoop] getCFRunLoop];
sockets = g_new (SocketInfo, nfds);
int i;
for (i = 0; i < nfds; i++)
{
SocketInfo *si = &sockets[i];
CFSocketCallBackType cbtype;
cbtype = 0;
if (ufds[i].events & G_IO_IN)
cbtype |= kCFSocketReadCallBack;
if (ufds[i].events & G_IO_OUT)
cbtype |= kCFSocketWriteCallBack;
/* FIXME: how to handle G_IO_HUP and G_IO_ERR? */
const CFSocketContext ctxt = {
0, si, NULL, NULL, NULL
};
si->sock = CFSocketCreateWithNative (NULL, ufds[i].fd, cbtype, socket_activity_cb, &ctxt);
si->source = CFSocketCreateRunLoopSource (NULL, si->sock, 0);
si->revents = 0;
CFRunLoopAddSource (run_loop, si->source, kCFRunLoopCommonModes);
}
}
nsevent = [NSApp nextEventMatchingMask: NSAnyEventMask
untilDate: until_date
inMode: NSDefaultRunLoopMode
dequeue: YES];
/* Push the events to NSApplication which will do some magic(?) and forward
* interesting events to our view. While we could do event translation here
* we'd also need to filter out clicks on titlebar, and perhaps do special
* handling for the first click (couldn't figure it out - always ended up
* missing a screen refresh) and maybe other things.
*/
[NSApp sendEvent:nsevent];
if (nfds > 0)
{
int i;
for (i = 0; i < nfds; i++)
{
SocketInfo *si = &sockets[i];
if ((ufds[i].revents = si->revents) != 0)
n_active++;
/* Invalidating the source also removes it from run loop and
* guarantees the callback is never called again.
* CFRunLoopRemoveSource removes the source from the loop, but might
* still call the callback which would be badly timed.
*/
CFRunLoopSourceInvalidate (si->source);
CFRelease (si->source);
CFRelease (si->sock);
}
g_free (sockets);
}
/* FIXME this could result in infinite loop */
ClutterEvent *event = clutter_event_get ();
while (event)
{
clutter_do_event (event);
clutter_event_free (event);
event = clutter_event_get ();
}
CLUTTER_OSX_POOL_RELEASE();
return n_active;
}
void
_clutter_events_osx_init (void)
{
g_assert (old_poll_func == NULL);
old_poll_func = g_main_context_get_poll_func (NULL);
g_main_context_set_poll_func (NULL, clutter_event_osx_poll_func);
_clutter_osx_event_loop_init ();
}
void
_clutter_events_osx_uninit (void)
{
if (old_poll_func)
{
g_main_context_set_poll_func (NULL, old_poll_func);
old_poll_func = NULL;
}
g_assert_not_reached ();
}

View File

@ -67,6 +67,9 @@ struct _ClutterStageOSX
ClutterStageState stage_state;
gboolean acceptFocus;
gfloat scroll_pos_x;
gfloat scroll_pos_y;
};
struct _ClutterStageOSXClass