mirror of
https://github.com/brl/mutter.git
synced 2024-12-23 19:42:05 +00:00
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:
parent
1558975bc3
commit
634e4ffd1a
@ -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
|
||||
|
@ -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 {
|
||||
|
1051
clutter/osx/clutter-event-loop-osx.c
Normal file
1051
clutter/osx/clutter-event-loop-osx.c
Normal file
File diff suppressed because it is too large
Load Diff
32
clutter/osx/clutter-event-loop-osx.h
Normal file
32
clutter/osx/clutter-event-loop-osx.h
Normal 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
|
@ -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 ();
|
||||
}
|
||||
|
@ -67,6 +67,9 @@ struct _ClutterStageOSX
|
||||
ClutterStageState stage_state;
|
||||
|
||||
gboolean acceptFocus;
|
||||
|
||||
gfloat scroll_pos_x;
|
||||
gfloat scroll_pos_y;
|
||||
};
|
||||
|
||||
struct _ClutterStageOSXClass
|
||||
|
Loading…
Reference in New Issue
Block a user