2008-03-25 Neil Roberts <neil@o-hand.com>

Added a native Win32 WGL backend.

	* configure.ac: Added the 'win32' flavour.

	* clutter/cogl/gl/cogl.c (cogl_get_proc_address): Added an ifdef
	to use wglGetProcAddress with the Win32 backend.

	* clutter/Makefile.am (DIST_SUBDIRS): Added the win32 directory.

	* clutter/win32/clutter-win32.pc.in:
	* clutter/win32/clutter-win32.h: 
	* clutter/win32/clutter-stage-win32.h: 
	* clutter/win32/clutter-stage-win32.c: 
	* clutter/win32/clutter-event-win32.c: 
	* clutter/win32/clutter-backend-win32.h: 
	* clutter/win32/Makefile.am:
	* clutter/win32/clutter-backend-win32.c: New files.
This commit is contained in:
Neil Roberts 2008-03-25 15:42:50 +00:00
parent 7c05499865
commit 051ed6b43b
12 changed files with 1831 additions and 7 deletions

View File

@ -1,3 +1,23 @@
2008-03-25 Neil Roberts <neil@o-hand.com>
Added a native Win32 WGL backend.
* configure.ac: Added the 'win32' flavour.
* clutter/cogl/gl/cogl.c (cogl_get_proc_address): Added an ifdef
to use wglGetProcAddress with the Win32 backend.
* clutter/Makefile.am (DIST_SUBDIRS): Added the win32 directory.
* clutter/win32/clutter-win32.pc.in:
* clutter/win32/clutter-win32.h:
* clutter/win32/clutter-stage-win32.h:
* clutter/win32/clutter-stage-win32.c:
* clutter/win32/clutter-event-win32.c:
* clutter/win32/clutter-backend-win32.h:
* clutter/win32/Makefile.am:
* clutter/win32/clutter-backend-win32.c: New files.
2008-03-19 Emmanuele Bassi <ebassi@openedhand.com>
* clutter/clutter-model.[ch]: Add a ::copy() virtual function

View File

@ -2,7 +2,7 @@ NULL =
SUBDIRS = cogl pango json $(clutterbackend) $(backendextra)
DIST_SUBDIRS = pango glx eglx eglnative cogl sdl json osx x11
DIST_SUBDIRS = pango glx eglx eglnative cogl sdl json osx x11 win32
target = $(clutterbackend)

View File

@ -109,7 +109,7 @@ cogl_get_proc_address (const gchar* name)
/* Sucks to ifdef here but not other option..? would be nice to
* split the code up for more reuse (once more backends use this
*/
#ifdef HAVE_CLUTTER_GLX
#if defined(HAVE_CLUTTER_GLX)
static GLXGetProcAddressProc get_proc_func = NULL;
static void *dlhand = NULL;
@ -142,7 +142,11 @@ cogl_get_proc_address (const gchar* name)
if (get_proc_func)
return get_proc_func ((unsigned char*) name);
#else /* !HAVE_CLUTTER_GLX */
#elif defined(HAVE_CLUTTER_WIN32)
return (CoglFuncPtr) wglGetProcAddress ((LPCSTR) name);
#else /* HAVE_CLUTTER_WIN32 */
/* this should find the right function if the program is linked against a
* library providing it */
@ -158,7 +162,7 @@ cogl_get_proc_address (const gchar* name)
return symbol;
}
#endif /* HAVE_CLUTTER_GLX */
#endif /* HAVE_CLUTTER_WIN32 */
return NULL;
}

33
clutter/win32/Makefile.am Normal file
View File

@ -0,0 +1,33 @@
libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/clutter
libclutterinclude_HEADERS = clutter-win32.h
clutter-win32-$(CLUTTER_API_VERSION).pc: clutter-win32.pc
@cp -f $< $(@F)
pkgconfig_DATA = clutter-win32-@CLUTTER_API_VERSION@.pc
pkgconfigdir = $(libdir)/pkgconfig
INCLUDES = \
-DG_LOG_DOMAIN=\"ClutterWin32\" \
-I$(top_srcdir) \
-I$(top_srcdir)/clutter/cogl \
-I$(top_srcdir)/clutter/cogl/@CLUTTER_COGL@ \
$(CLUTTER_CFLAGS) \
$(CLUTTER_DEBUG_CFLAGS) \
$(GCC_FLAGS)
LDADD = $(CLUTTER_LIBS)
noinst_LTLIBRARIES = libclutter-win32.la
libclutter_win32_la_SOURCES = \
clutter-backend-win32.h \
clutter-backend-win32.c \
clutter-event-win32.c \
clutter-stage-win32.h \
clutter-stage-win32.c \
clutter-win32.h
CLEANFILES = clutter-win32-$(CLUTTER_API_VERSION).pc
EXTRA_DIST = clutter-win32.pc.in

View File

@ -0,0 +1,302 @@
/* Clutter.
* An OpenGL based 'interactive canvas' library.
* Authored By Matthew Allum <mallum@openedhand.com>
* Copyright (C) 2006-2007 OpenedHand
*
* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <windows.h>
#include "clutter-backend-win32.h"
#include "clutter-stage-win32.h"
#include "clutter-win32.h"
#include "../clutter-event.h"
#include "../clutter-main.h"
#include "../clutter-debug.h"
#include "../clutter-private.h"
#include "cogl.h"
G_DEFINE_TYPE (ClutterBackendWin32, clutter_backend_win32,
CLUTTER_TYPE_BACKEND);
typedef int (* SwapIntervalProc) (int interval);
/* singleton object */
static ClutterBackendWin32 *backend_singleton = NULL;
static gchar *clutter_vblank_name = NULL;
gboolean
clutter_backend_win32_pre_parse (ClutterBackend *backend,
GError **error)
{
const gchar *env_string;
if ((env_string = g_getenv ("CLUTTER_VBLANK")))
clutter_vblank_name = g_strdup (env_string);
return TRUE;
}
static void
clutter_backend_win32_init_events (ClutterBackend *backend)
{
CLUTTER_NOTE (EVENT, "initialising the event loop");
_clutter_backend_win32_events_init (backend);
}
ClutterActor *
clutter_backend_win32_get_stage (ClutterBackend *backend)
{
ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
return backend_win32->stage;
}
static const GOptionEntry entries[] =
{
{
"vblank", 0,
0,
G_OPTION_ARG_STRING, &clutter_vblank_name,
"VBlank method to be used (none, default or wgl)", "METHOD"
},
{ NULL }
};
void
clutter_backend_win32_add_options (ClutterBackend *backend,
GOptionGroup *group)
{
g_option_group_add_entries (group, entries);
}
static void
clutter_backend_win32_finalize (GObject *gobject)
{
backend_singleton = NULL;
G_OBJECT_CLASS (clutter_backend_win32_parent_class)->finalize (gobject);
}
static void
clutter_backend_win32_dispose (GObject *gobject)
{
ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (gobject);
if (backend_win32->stage)
{
CLUTTER_NOTE (BACKEND, "Disposing the main stage");
/* we unset the private flag on the stage so we can safely
* destroy it without a warning from clutter_actor_destroy()
*/
CLUTTER_UNSET_PRIVATE_FLAGS (backend_win32->stage,
CLUTTER_ACTOR_IS_TOPLEVEL);
clutter_actor_destroy (backend_win32->stage);
backend_win32->stage = NULL;
}
CLUTTER_NOTE (BACKEND, "Removing the event source");
_clutter_backend_win32_events_uninit (CLUTTER_BACKEND (backend_win32));
G_OBJECT_CLASS (clutter_backend_win32_parent_class)->dispose (gobject);
}
static GObject *
clutter_backend_win32_constructor (GType gtype,
guint n_params,
GObjectConstructParam *params)
{
GObjectClass *parent_class;
GObject *retval;
if (!backend_singleton)
{
parent_class = G_OBJECT_CLASS (clutter_backend_win32_parent_class);
retval = parent_class->constructor (gtype, n_params, params);
backend_singleton = CLUTTER_BACKEND_WIN32 (retval);
return retval;
}
g_warning ("Attempting to create a new backend object. This should "
"never happen, so we return the singleton instance.");
return g_object_ref (backend_singleton);
}
static gboolean
check_vblank_env (const char *name)
{
return clutter_vblank_name && !strcasecmp (clutter_vblank_name, name);
}
ClutterFeatureFlags
clutter_backend_win32_get_features (ClutterBackend *backend)
{
ClutterFeatureFlags flags;
const gchar *extensions;
SwapIntervalProc swap_interval;
/* FIXME: we really need to check if gl context is set */
extensions = glGetString (GL_EXTENSIONS);
CLUTTER_NOTE (BACKEND, "Checking features\n"
"GL_VENDOR: %s\n"
"GL_RENDERER: %s\n"
"GL_VERSION: %s\n"
"GL_EXTENSIONS: %s\n",
glGetString (GL_VENDOR),
glGetString (GL_RENDERER),
glGetString (GL_VERSION),
extensions);
flags = CLUTTER_FEATURE_STAGE_USER_RESIZE | CLUTTER_FEATURE_STAGE_CURSOR;
/* If the VBlank should be left at the default or it has been
disabled elsewhere (eg NVIDIA) then don't bother trying to check
for the swap control extension */
if (getenv ("__GL_SYNC_TO_VBLANK") || check_vblank_env ("default"))
CLUTTER_NOTE (BACKEND, "vblank sync: left at default at user request");
else if (cogl_check_extension ("WGL_EXT_swap_control", extensions)
&& (swap_interval = (SwapIntervalProc)
cogl_get_proc_address ("wglSwapIntervalEXT")))
{
/* According to the specification for the WGL_EXT_swap_control
extension the default swap interval is 1 anyway, so if no
vblank is requested then we should explicitly set it to
zero */
if (check_vblank_env ("none"))
{
if (swap_interval (0))
CLUTTER_NOTE (BACKEND, "vblank sync: successfully disabled");
else
CLUTTER_NOTE (BACKEND, "vblank sync: disabling failed");
}
else
{
if (swap_interval (1))
{
flags |= CLUTTER_FEATURE_SYNC_TO_VBLANK;
CLUTTER_NOTE (BACKEND, "vblank sync: wglSwapIntervalEXT "
"vblank setup success");
}
else
CLUTTER_NOTE (BACKEND, "vblank sync: wglSwapIntervalEXT "
"vblank setup failed");
}
}
else
CLUTTER_NOTE (BACKEND, "no use-able vblank mechanism found");
return flags;
}
static void
clutter_backend_win32_redraw (ClutterBackend *backend)
{
ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
ClutterStageWin32 *stage_win32;
stage_win32 = CLUTTER_STAGE_WIN32 (backend_win32->stage);
clutter_actor_paint (CLUTTER_ACTOR (stage_win32));
if (stage_win32->client_dc)
SwapBuffers (stage_win32->client_dc);
}
static gboolean
clutter_backend_win32_init_stage (ClutterBackend *backend,
GError **error)
{
ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
if (!backend_win32->stage)
{
ClutterStageWin32 *stage_win32;
ClutterActor *stage;
stage = g_object_new (CLUTTER_TYPE_STAGE_WIN32, NULL);
/* copy backend data into the stage */
stage_win32 = CLUTTER_STAGE_WIN32 (stage);
stage_win32->backend = backend_win32;
g_object_set_data (G_OBJECT (stage), "clutter-backend", backend);
backend_win32->stage = g_object_ref_sink (stage);
}
clutter_actor_realize (backend_win32->stage);
if (!CLUTTER_ACTOR_IS_REALIZED (backend_win32->stage))
{
g_set_error (error, CLUTTER_INIT_ERROR,
CLUTTER_INIT_ERROR_INTERNAL,
"Unable to realize the main stage");
return FALSE;
}
return TRUE;
}
static void
clutter_backend_win32_class_init (ClutterBackendWin32Class *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass);
gobject_class->constructor = clutter_backend_win32_constructor;
gobject_class->dispose = clutter_backend_win32_dispose;
gobject_class->finalize = clutter_backend_win32_finalize;
backend_class->pre_parse = clutter_backend_win32_pre_parse;
backend_class->init_events = clutter_backend_win32_init_events;
backend_class->init_stage = clutter_backend_win32_init_stage;
backend_class->get_stage = clutter_backend_win32_get_stage;
backend_class->add_options = clutter_backend_win32_add_options;
backend_class->get_features = clutter_backend_win32_get_features;
backend_class->redraw = clutter_backend_win32_redraw;
}
static void
clutter_backend_win32_init (ClutterBackendWin32 *backend_win32)
{
ClutterBackend *backend = CLUTTER_BACKEND (backend_win32);
/* FIXME: get from GetSystemMetric? */
clutter_backend_set_double_click_time (backend, 250);
clutter_backend_set_double_click_distance (backend, 5);
clutter_backend_set_resolution (backend, 96.0);
}
GType
_clutter_backend_impl_get_type (void)
{
return clutter_backend_win32_get_type ();
}

View File

@ -0,0 +1,76 @@
/* Clutter.
* An OpenGL based 'interactive canvas' library.
* Authored By Matthew Allum <mallum@openedhand.com>
* Copyright (C) 2006-2007 OpenedHand
*
* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CLUTTER_BACKEND_WIN32_H__
#define __CLUTTER_BACKEND_WIN32_H__
#include <glib-object.h>
#include <clutter/clutter-event.h>
#include <clutter/clutter-backend.h>
#include <windows.h>
#include "clutter-win32.h"
G_BEGIN_DECLS
#define CLUTTER_TYPE_BACKEND_WIN32 (clutter_backend_win32_get_type ())
#define CLUTTER_BACKEND_WIN32(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BACKEND_WIN32, ClutterBackendWin32))
#define CLUTTER_IS_BACKEND_WIN32(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BACKEND_WIN32))
#define CLUTTER_BACKEND_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BACKEND_WIN32, ClutterBackendWin32Class))
#define CLUTTER_IS_BACKEND_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BACKEND_WIN32))
#define CLUTTER_BACKEND_WIN32_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BACKEND_WIN32, ClutterBackendWin32Class))
typedef struct _ClutterBackendWin32 ClutterBackendWin32;
typedef struct _ClutterBackendWin32Class ClutterBackendWin32Class;
struct _ClutterBackendWin32
{
ClutterBackend parent_instance;
/* main stage singleton */
ClutterActor *stage;
GSource *event_source;
};
struct _ClutterBackendWin32Class
{
ClutterBackendClass parent_class;
};
void _clutter_backend_win32_events_init (ClutterBackend *backend);
void _clutter_backend_win32_events_uninit (ClutterBackend *backend);
GType clutter_backend_win32_get_type (void) G_GNUC_CONST;
ClutterActor *
clutter_backend_win32_get_stage (ClutterBackend *backend);
void
clutter_backend_win32_add_options (ClutterBackend *backend,
GOptionGroup *group);
ClutterFeatureFlags
clutter_backend_win32_get_features (ClutterBackend *backend);
G_END_DECLS
#endif /* __CLUTTER_BACKEND_WIN32_H__ */

View File

@ -0,0 +1,613 @@
/* Clutter.
* An OpenGL based 'interactive canvas' library.
* Authored By Matthew Allum <mallum@openedhand.com>
* Copyright (C) 2006-2007 OpenedHand
*
* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include "clutter-stage-win32.h"
#include "clutter-backend-win32.h"
#include "clutter-win32.h"
#include "../clutter-backend.h"
#include "../clutter-event.h"
#include "../clutter-private.h"
#include "../clutter-debug.h"
#include "../clutter-main.h"
#include "../clutter-keysyms.h"
#include <string.h>
#include <glib.h>
#include <windows.h>
#include <windowsx.h>
typedef struct _ClutterEventSource ClutterEventSource;
struct _ClutterEventSource
{
GSource source;
ClutterBackend *backend;
GPollFD event_poll_fd;
};
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 GSourceFuncs event_funcs = {
clutter_event_prepare,
clutter_event_check,
clutter_event_dispatch,
NULL
};
/* Special mapping for some keys that don't have a direct Unicode
value. Must be sorted by the numeric value of the Windows key
virtual key code */
static const struct
{
gushort win_sym, clutter_sym;
} clutter_win32_key_map[] =
{
{ VK_CANCEL, CLUTTER_Cancel },
{ VK_BACK, CLUTTER_BackSpace },
{ VK_TAB, CLUTTER_Tab },
{ VK_CLEAR, CLUTTER_Clear },
{ VK_RETURN, CLUTTER_Return },
{ VK_MENU, CLUTTER_Menu },
{ VK_PAUSE, CLUTTER_Pause },
{ VK_HANGUL, CLUTTER_Hangul },
{ VK_KANJI, CLUTTER_Kanji },
{ VK_ESCAPE, CLUTTER_Escape },
{ VK_SPACE, CLUTTER_space },
{ VK_PRIOR, CLUTTER_Prior },
{ VK_NEXT, CLUTTER_Next },
{ VK_END, CLUTTER_End },
{ VK_HOME, CLUTTER_Home },
{ VK_LEFT, CLUTTER_Left },
{ VK_UP, CLUTTER_Up },
{ VK_RIGHT, CLUTTER_Right },
{ VK_DOWN, CLUTTER_Down },
{ VK_SELECT, CLUTTER_Select },
{ VK_PRINT, CLUTTER_Print },
{ VK_EXECUTE, CLUTTER_Execute },
{ VK_INSERT, CLUTTER_Insert },
{ VK_DELETE, CLUTTER_Delete },
{ VK_HELP, CLUTTER_Help },
{ VK_MULTIPLY, CLUTTER_multiply },
{ VK_F1, CLUTTER_F1 },
{ VK_F2, CLUTTER_F2 },
{ VK_F3, CLUTTER_F3 },
{ VK_F4, CLUTTER_F4 },
{ VK_F5, CLUTTER_F5 },
{ VK_F6, CLUTTER_F6 },
{ VK_F7, CLUTTER_F7 },
{ VK_F8, CLUTTER_F8 },
{ VK_F9, CLUTTER_F9 },
{ VK_F10, CLUTTER_F10 },
{ VK_F11, CLUTTER_F11 },
{ VK_F12, CLUTTER_F12 },
{ VK_F13, CLUTTER_F13 },
{ VK_F14, CLUTTER_F14 },
{ VK_F15, CLUTTER_F15 },
{ VK_F16, CLUTTER_F16 },
{ VK_F17, CLUTTER_F17 },
{ VK_F18, CLUTTER_F18 },
{ VK_F19, CLUTTER_F19 },
{ VK_F20, CLUTTER_F20 },
{ VK_F21, CLUTTER_F21 },
{ VK_F22, CLUTTER_F22 },
{ VK_F23, CLUTTER_F23 },
{ VK_F24, CLUTTER_F24 },
{ VK_LSHIFT, CLUTTER_Shift_L },
{ VK_RSHIFT, CLUTTER_Shift_R },
{ VK_LCONTROL, CLUTTER_Control_L },
{ VK_RCONTROL, CLUTTER_Control_R }
};
#define CLUTTER_WIN32_KEY_MAP_SIZE (sizeof (clutter_win32_key_map) \
/ sizeof (clutter_win32_key_map[0]))
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;
}
void
_clutter_backend_win32_events_init (ClutterBackend *backend)
{
ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
GSource *source;
ClutterEventSource *event_source;
source = backend_win32->event_source = clutter_event_source_new (backend);
event_source = (ClutterEventSource *) source;
g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS);
event_source->event_poll_fd.fd = G_WIN32_MSG_HANDLE;
event_source->event_poll_fd.events = G_IO_IN;
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_win32_events_uninit (ClutterBackend *backend)
{
ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
if (backend_win32->event_source)
{
CLUTTER_NOTE (EVENT, "Destroying the event source");
g_source_destroy (backend_win32->event_source);
g_source_unref (backend_win32->event_source);
backend_win32->event_source = NULL;
}
}
static gboolean
check_msg_pending ()
{
MSG msg;
return PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE) ? TRUE : FALSE;
}
static ClutterModifierType
get_modifier_state (WPARAM wparam)
{
ClutterModifierType ret = 0;
if ((wparam & MK_SHIFT))
ret |= CLUTTER_SHIFT_MASK;
if ((wparam & MK_CONTROL))
ret |= CLUTTER_CONTROL_MASK;
if ((wparam & MK_LBUTTON))
ret |= CLUTTER_BUTTON1_MASK;
if ((wparam & MK_MBUTTON))
ret |= CLUTTER_BUTTON2_MASK;
if ((wparam & MK_RBUTTON))
ret |= CLUTTER_BUTTON3_MASK;
return ret;
}
static void
make_button_event (const MSG *msg, ClutterEvent *event,
int button, int click_count, gboolean release)
{
event->type = release ? CLUTTER_BUTTON_RELEASE : CLUTTER_BUTTON_PRESS;
event->button.time = msg->time;
event->button.x = GET_X_LPARAM (msg->lParam);
event->button.y = GET_Y_LPARAM (msg->lParam);
event->button.modifier_state = get_modifier_state (msg->wParam);
event->button.button = button;
event->button.click_count = click_count;
}
static gboolean
clutter_event_prepare (GSource *source,
gint *timeout)
{
gboolean retval;
clutter_threads_enter ();
*timeout = -1;
retval = (clutter_events_pending () || check_msg_pending ());
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_msg_pending (backend));
else
retval = FALSE;
clutter_threads_leave ();
return retval;
}
static gboolean
clutter_event_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
ClutterEvent *event;
MSG msg;
clutter_threads_enter ();
/* Process Windows messages until we've got one that translates into
the clutter event queue */
while (!clutter_events_pending () && PeekMessageW (&msg, NULL,
0, 0, PM_REMOVE))
DispatchMessageW (&msg);
/* Pop an event off the queue if any */
if ((event = clutter_event_get ()))
{
/* forward the event into clutter for emission etc. */
clutter_do_event (event);
clutter_event_free (event);
}
clutter_threads_leave ();
return TRUE;
}
static ClutterModifierType
get_key_modifier_state (const BYTE *key_states)
{
ClutterModifierType ret = 0;
if ((key_states[VK_SHIFT] & 0x80)
|| (key_states[VK_LSHIFT] & 0x80)
|| (key_states[VK_RSHIFT] & 0x80))
ret |= CLUTTER_SHIFT_MASK;
if ((key_states[VK_CONTROL] & 0x80)
|| (key_states[VK_LCONTROL] & 0x80)
|| (key_states[VK_RCONTROL] & 0x80))
ret |= CLUTTER_CONTROL_MASK;
if ((key_states[VK_MENU] & 0x80)
|| (key_states[VK_LMENU] & 0x80)
|| (key_states[VK_RMENU] & 0x80))
ret |= CLUTTER_MOD1_MASK;
if (key_states[VK_CAPITAL])
ret |= CLUTTER_LOCK_MASK;
return ret;
}
static gboolean
message_translate (ClutterBackend *backend,
ClutterEvent *event,
const MSG *msg)
{
ClutterBackendWin32 *backend_win32;
ClutterStageWin32 *stage_win32;
ClutterStage *stage;
gboolean res;
HWND stage_hwnd;
backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
stage = CLUTTER_STAGE (_clutter_backend_get_stage (backend));
stage_win32 = CLUTTER_STAGE_WIN32 (stage);
stage_hwnd = clutter_win32_get_stage_window (stage);
/* Do further processing only on events for the stage window */
if (stage_hwnd != msg->hwnd)
return FALSE;
res = TRUE;
switch (msg->message)
{
case WM_SIZE:
{
WORD new_width = LOWORD (msg->lParam);
WORD new_height = HIWORD (msg->lParam);
guint old_width, old_height;
clutter_actor_get_size (CLUTTER_ACTOR (stage),
&old_width, &old_height);
if (new_width != old_width || new_height != old_height)
clutter_actor_set_size (CLUTTER_ACTOR (stage),
new_width, new_height);
}
res = FALSE;
break;
case WM_MOVE:
{
WORD new_xpos = GET_X_LPARAM (msg->lParam);
WORD new_ypos = GET_Y_LPARAM (msg->lParam);
guint old_xpos, old_ypos;
clutter_actor_get_position (CLUTTER_ACTOR (stage),
&old_xpos, &old_ypos);
if (new_xpos != old_xpos || new_ypos != old_ypos)
clutter_actor_set_position (CLUTTER_ACTOR (stage),
new_xpos, new_ypos);
}
res = FALSE;
break;
case WM_SHOWWINDOW:
if (msg->wParam)
clutter_stage_win32_map (stage_win32);
else
clutter_stage_win32_unmap (stage_win32);
res = FALSE;
break;
case WM_ACTIVATE:
if (msg->wParam == WA_INACTIVE)
{
if (stage_win32->state & CLUTTER_STAGE_STATE_ACTIVATED)
{
stage_win32->state &= ~CLUTTER_STAGE_STATE_ACTIVATED;
event->type = CLUTTER_STAGE_STATE;
event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED;
event->stage_state.new_state = stage_win32->state;
}
else
res = FALSE;
break;
}
else
{
if (!(stage_win32->state & CLUTTER_STAGE_STATE_ACTIVATED))
{
stage_win32->state |= CLUTTER_STAGE_STATE_ACTIVATED;
event->type = CLUTTER_STAGE_STATE;
event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED;
event->stage_state.new_state = stage_win32->state;
}
else
res = FALSE;
}
break;
case WM_PAINT:
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage_win32));
res = FALSE;
break;
case WM_DESTROY:
CLUTTER_NOTE (EVENT, "WM_DESTROY");
event->type = CLUTTER_DESTROY_NOTIFY;
break;
case WM_LBUTTONDOWN:
make_button_event (msg, event, 1, 1, FALSE);
break;
case WM_MBUTTONDOWN:
make_button_event (msg, event, 2, 1, FALSE);
break;
case WM_RBUTTONDOWN:
make_button_event (msg, event, 3, 1, FALSE);
break;
case WM_LBUTTONUP:
make_button_event (msg, event, 1, 1, TRUE);
break;
case WM_MBUTTONUP:
make_button_event (msg, event, 2, 1, TRUE);
break;
case WM_RBUTTONUP:
make_button_event (msg, event, 3, 1, TRUE);
break;
case WM_LBUTTONDBLCLK:
make_button_event (msg, event, 1, 2, FALSE);
break;
case WM_MBUTTONDBLCLK:
make_button_event (msg, event, 2, 2, FALSE);
break;
case WM_RBUTTONDBLCLK:
make_button_event (msg, event, 3, 2, FALSE);
break;
case WM_MOUSEWHEEL:
stage_win32->scroll_pos += (SHORT) HIWORD (msg->wParam);
event->type = CLUTTER_SCROLL;
event->scroll.time = msg->time;
event->scroll.x = GET_X_LPARAM (msg->lParam);
event->scroll.y = GET_Y_LPARAM (msg->lParam);
event->scroll.modifier_state
= get_modifier_state (LOWORD (msg->wParam));
if (stage_win32->scroll_pos >= WHEEL_DELTA)
{
event->scroll.direction = CLUTTER_SCROLL_UP;
stage_win32->scroll_pos -= WHEEL_DELTA;
}
else if (stage_win32->scroll_pos <= -WHEEL_DELTA)
{
event->scroll.direction = CLUTTER_SCROLL_DOWN;
stage_win32->scroll_pos += WHEEL_DELTA;
}
else
res = FALSE;
break;
case WM_MOUSEMOVE:
event->type = CLUTTER_MOTION;
event->motion.time = msg->time;
event->motion.x = GET_X_LPARAM (msg->lParam);
event->motion.y = GET_Y_LPARAM (msg->lParam);
event->motion.modifier_state = get_modifier_state (msg->wParam);
break;
case WM_KEYDOWN:
case WM_KEYUP:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
{
int scan_code = (msg->lParam >> 16) & 0xff;
int min = 0, max = CLUTTER_WIN32_KEY_MAP_SIZE, mid;
BYTE key_states[256];
/* Get the keyboard modifier states. GetKeyboardState
conveniently gets the key state that was current when the
last keyboard message read was generated */
GetKeyboardState(key_states);
/* Binary chop to check if we have a direct mapping for this
key code */
while (min < max)
{
mid = (min + max) / 2;
if (clutter_win32_key_map[mid].win_sym == msg->wParam)
{
event->key.keyval = clutter_win32_key_map[mid].clutter_sym;
event->key.unicode_value = 0;
break;
}
else if (clutter_win32_key_map[mid].win_sym < msg->wParam)
min = mid + 1;
else
max = mid;
}
/* If we don't have a direct mapping then try getting the
unicode value of the key sym */
if (min >= max)
{
WCHAR ch;
BYTE shift_state[256];
/* Translate to a Unicode value, but only take into
account the shift key. That way Ctrl+Shift+C will
generate a capital C virtual key code with a zero
unicode value for example */
memset (shift_state, 0, 256);
shift_state[VK_SHIFT] = key_states[VK_SHIFT];
shift_state[VK_LSHIFT] = key_states[VK_LSHIFT];
shift_state[VK_RSHIFT] = key_states[VK_RSHIFT];
shift_state[VK_CAPITAL] = key_states[VK_CAPITAL];
if (ToUnicode (msg->wParam, scan_code,
shift_state, &ch, 1, 0) == 1
/* The codes in this range directly match the Latin 1
codes so we can just use the Unicode value as the
key sym */
&& ch >= 0x20 && ch <= 0xff)
event->key.keyval = ch;
else
/* Otherwise we don't know what the key means but the
application might be able to do something with the
scan code so we might as well still generate the
event */
event->key.keyval = CLUTTER_VoidSymbol;
/* Get the unicode value of the keypress again using the
full modifier state */
if (ToUnicode (msg->wParam, scan_code,
key_states, &ch, 1, 0) == 1)
event->key.unicode_value = ch;
else
event->key.unicode_value = 0;
}
event->key.type = msg->message == WM_KEYDOWN
|| msg->message == WM_SYSKEYDOWN
? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE;
event->key.time = msg->time;
event->key.modifier_state = get_key_modifier_state (key_states);
event->key.hardware_keycode = scan_code;
}
break;
default:
/* ignore every other message */
res = FALSE;
break;
}
return res;
}
LRESULT CALLBACK
_clutter_stage_win32_window_proc (HWND hwnd, UINT umsg,
WPARAM wparam, LPARAM lparam)
{
ClutterStageWin32 *stage_win32
= (ClutterStageWin32 *) GetWindowLongPtrW (hwnd, 0);
/* Ignore any messages before SetWindowLongPtr has been called to
set the stage */
if (stage_win32 != NULL)
{
ClutterBackendWin32 *backend_win32 = stage_win32->backend;
MSG msg;
ClutterEvent *event;
ClutterMainContext *clutter_context;
DWORD message_pos = GetMessagePos ();
clutter_context = clutter_context_get_default ();
msg.hwnd = hwnd;
msg.message = umsg;
msg.wParam = wparam;
msg.lParam = lparam;
msg.time = GetMessageTime ();
/* Neither MAKE_POINTS nor GET_[XY]_LPARAM is defined in MinGW
headers so we need to convert to a signed type explicitly */
msg.pt.x = (SHORT) LOWORD (message_pos);
msg.pt.y = (SHORT) HIWORD (message_pos);
/* Some messages are handled here specially outside of
message_translate so that DefWindowProc can be overridden */
if (umsg == WM_GETMINMAXINFO)
{
MINMAXINFO *min_max_info = (MINMAXINFO *) lparam;
_clutter_stage_win32_get_min_max_info (stage_win32, min_max_info);
return 0;
}
else
{
event = clutter_event_new (CLUTTER_NOTHING);
if (message_translate (CLUTTER_BACKEND (backend_win32), event, &msg))
/* push directly here to avoid copy of queue_put */
g_queue_push_head (clutter_context->events_queue, event);
else
clutter_event_free (event);
}
}
return DefWindowProcW (hwnd, umsg, wparam, lparam);
}

View File

@ -0,0 +1,614 @@
/* Clutter.
* An OpenGL based 'interactive canvas' library.
* Authored By Matthew Allum <mallum@openedhand.com>
* Copyright (C) 2006-2007 OpenedHand
*
* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "clutter-backend-win32.h"
#include "clutter-stage-win32.h"
#include "clutter-win32.h"
#include "../clutter-main.h"
#include "../clutter-feature.h"
#include "../clutter-color.h"
#include "../clutter-util.h"
#include "../clutter-event.h"
#include "../clutter-enum-types.h"
#include "../clutter-private.h"
#include "../clutter-debug.h"
#include "../clutter-units.h"
#include "cogl.h"
#include <windows.h>
G_DEFINE_TYPE (ClutterStageWin32, clutter_stage_win32, CLUTTER_TYPE_STAGE);
static void
clutter_stage_win32_show (ClutterActor *actor)
{
ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (actor);
if (stage_win32->hwnd)
ShowWindow (stage_win32->hwnd, SW_SHOW);
/* chain up */
CLUTTER_ACTOR_CLASS (clutter_stage_win32_parent_class)->show (actor);
}
static void
clutter_stage_win32_hide (ClutterActor *actor)
{
ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (actor);
if (stage_win32->hwnd)
ShowWindow (stage_win32->hwnd, SW_HIDE);
/* chain up */
CLUTTER_ACTOR_CLASS (clutter_stage_win32_parent_class)->hide (actor);
}
static void
clutter_stage_win32_query_coords (ClutterActor *self,
ClutterActorBox *box)
{
ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (self);
/* If we're in fullscreen mode then return the size of the screen
instead */
if ((stage_win32->state & CLUTTER_STAGE_STATE_FULLSCREEN))
{
box->x1 = CLUTTER_UNITS_FROM_INT (stage_win32->fullscreen_rect.left);
box->y1 = CLUTTER_UNITS_FROM_INT (stage_win32->fullscreen_rect.top);
box->x2 = CLUTTER_UNITS_FROM_INT (stage_win32->fullscreen_rect.right);
box->y2 = CLUTTER_UNITS_FROM_INT (stage_win32->fullscreen_rect.bottom);
}
else
{
box->x1 = CLUTTER_UNITS_FROM_INT (stage_win32->win_xpos);
box->y1 = CLUTTER_UNITS_FROM_INT (stage_win32->win_ypos);
box->x2 = box->x1 + CLUTTER_UNITS_FROM_INT (stage_win32->win_width);
box->y2 = box->y1 + CLUTTER_UNITS_FROM_INT (stage_win32->win_height);
}
}
static void
get_fullscreen_rect (ClutterStageWin32 *stage_win32)
{
HMONITOR monitor;
MONITORINFO monitor_info;
/* If we already have a window then try to use the same monitor that
is already on */
if (stage_win32->hwnd)
monitor = MonitorFromWindow (stage_win32->hwnd, MONITOR_DEFAULTTONEAREST);
else
{
/* Otherwise just guess that they will want the monitor where
the cursor is */
POINT cursor;
GetCursorPos (&cursor);
monitor = MonitorFromPoint (cursor, MONITOR_DEFAULTTONEAREST);
}
monitor_info.cbSize = sizeof (monitor_info);
GetMonitorInfoW (monitor, &monitor_info);
stage_win32->fullscreen_rect = monitor_info.rcMonitor;
}
static void
get_full_window_pos (ClutterStageWin32 *stage_win32,
int xpos_in, int ypos_in,
int *xpos_out, int *ypos_out)
{
gboolean resizable
= clutter_stage_get_user_resizable (CLUTTER_STAGE (stage_win32));
/* The window position passed to CreateWindow includes the window
decorations */
*xpos_out = xpos_in - GetSystemMetrics (resizable ? SM_CXSIZEFRAME
: SM_CXFIXEDFRAME);
*ypos_out = ypos_in - GetSystemMetrics (resizable ? SM_CYSIZEFRAME
: SM_CYFIXEDFRAME)
- GetSystemMetrics (SM_CYCAPTION);
}
static void
get_full_window_size (ClutterStageWin32 *stage_win32,
int width_in, int height_in,
int *width_out, int *height_out)
{
gboolean resizable
= clutter_stage_get_user_resizable (CLUTTER_STAGE (stage_win32));
/* The window size passed to CreateWindow includes the window
decorations */
*width_out = width_in + GetSystemMetrics (resizable ? SM_CXSIZEFRAME
: SM_CXFIXEDFRAME) * 2;
*height_out = height_in + GetSystemMetrics (resizable ? SM_CYSIZEFRAME
: SM_CYFIXEDFRAME) * 2
+ GetSystemMetrics (SM_CYCAPTION);
}
void
_clutter_stage_win32_get_min_max_info (ClutterStageWin32 *stage_win32,
MINMAXINFO *min_max_info)
{
/* If the window isn't resizable then set the max and min size to
the current size */
if (!clutter_stage_get_user_resizable (CLUTTER_STAGE (stage_win32)))
{
int full_width, full_height;
get_full_window_size (stage_win32,
stage_win32->win_width, stage_win32->win_height,
&full_width, &full_height);
min_max_info->ptMaxTrackSize.x = full_width;
min_max_info->ptMinTrackSize.x = full_width;
min_max_info->ptMaxTrackSize.y = full_height;
min_max_info->ptMinTrackSize.y = full_height;
}
}
static void
clutter_stage_win32_request_coords (ClutterActor *self,
ClutterActorBox *box)
{
ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (self);
gint new_xpos, new_ypos, new_width, new_height;
new_xpos = CLUTTER_UNITS_TO_INT (MIN (box->x1, box->x2));
new_ypos = CLUTTER_UNITS_TO_INT (MIN (box->y1, box->y2));
new_width = ABS (CLUTTER_UNITS_TO_INT (box->x2 - box->x1));
new_height = ABS (CLUTTER_UNITS_TO_INT (box->y2 - box->y1));
if ((new_width != stage_win32->win_width
|| new_height != stage_win32->win_height
|| new_xpos != stage_win32->win_xpos
|| new_ypos != stage_win32->win_ypos)
/* Ignore size requests if we are in full screen mode */
&& (stage_win32->state & CLUTTER_STAGE_STATE_FULLSCREEN) == 0)
{
stage_win32->win_xpos = new_xpos;
stage_win32->win_ypos = new_ypos;
stage_win32->win_width = new_width;
stage_win32->win_height = new_height;
if (stage_win32->hwnd != NULL)
{
int full_xpos, full_ypos, full_width, full_height;
get_full_window_pos (stage_win32,
new_xpos, new_ypos,
&full_xpos, &full_ypos);
get_full_window_size (stage_win32,
new_width, new_height,
&full_width, &full_height);
SetWindowPos (stage_win32->hwnd, NULL,
full_xpos, full_ypos,
full_width, full_height,
SWP_NOZORDER);
}
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_SYNC_MATRICES);
}
CLUTTER_ACTOR_CLASS (clutter_stage_win32_parent_class)
->request_coords (self, box);
}
static void
clutter_stage_win32_set_title (ClutterStage *stage,
const gchar *title)
{
ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (stage);
wchar_t *wtitle;
/* Empty window titles not allowed, so set it to just a period. */
if (title == NULL || !title[0])
title = ".";
wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL);
SetWindowTextW (stage_win32->hwnd, wtitle);
g_free (wtitle);
}
static LONG
get_window_style (ClutterStageWin32 *stage_win32)
{
/* Fullscreen mode shouldn't have any borders */
if ((stage_win32->state & CLUTTER_STAGE_STATE_FULLSCREEN))
return WS_POPUP;
/* Otherwise it's an overlapped window but if it isn't resizable
then it shouldn't have a thick frame */
else if (clutter_stage_get_user_resizable (CLUTTER_STAGE (stage_win32)))
return WS_OVERLAPPEDWINDOW;
else
return WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME;
}
static void
clutter_stage_win32_set_user_resize (ClutterStage *stage,
gboolean value)
{
HWND hwnd = CLUTTER_STAGE_WIN32 (stage)->hwnd;
LONG old_style = GetWindowLongW (hwnd, GWL_STYLE);
/* Update the window style but preserve the visibility */
SetWindowLongW (hwnd, GWL_STYLE,
get_window_style (CLUTTER_STAGE_WIN32 (stage))
| (old_style & WS_VISIBLE));
}
static void
clutter_stage_win32_set_fullscreen (ClutterStage *stage,
gboolean value)
{
ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (stage);
HWND hwnd = CLUTTER_STAGE_WIN32 (stage)->hwnd;
LONG old_style = GetWindowLongW (hwnd, GWL_STYLE);
ClutterStageStateEvent event;
if (value)
stage_win32->state |= CLUTTER_STAGE_STATE_FULLSCREEN;
else
stage_win32->state &= ~CLUTTER_STAGE_STATE_FULLSCREEN;
if (hwnd)
{
/* Update the window style but preserve the visibility */
SetWindowLongW (hwnd, GWL_STYLE,
get_window_style (stage_win32)
| (old_style & WS_VISIBLE));
/* Update the window size */
if (value)
{
get_fullscreen_rect (stage_win32);
SetWindowPos (hwnd, HWND_TOP,
stage_win32->fullscreen_rect.left,
stage_win32->fullscreen_rect.top,
stage_win32->fullscreen_rect.right
- stage_win32->fullscreen_rect.left,
stage_win32->fullscreen_rect.bottom
- stage_win32->fullscreen_rect.top,
0);
}
else
{
int full_xpos, full_ypos, full_width, full_height;
get_full_window_pos (stage_win32,
stage_win32->win_xpos,
stage_win32->win_ypos,
&full_xpos, &full_ypos);
get_full_window_size (stage_win32,
stage_win32->win_width,
stage_win32->win_height,
&full_width, &full_height);
SetWindowPos (stage_win32->hwnd, NULL,
full_xpos, full_ypos,
full_width, full_height,
SWP_NOZORDER);
}
CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES);
}
/* Report the state change */
memset (&event, 0, sizeof (event));
event.type = CLUTTER_STAGE_STATE;
event.new_state = stage_win32->state;
event.changed_mask = CLUTTER_STAGE_STATE_FULLSCREEN;
clutter_event_put ((ClutterEvent *) &event);
}
static ATOM
clutter_stage_win32_get_window_class ()
{
static ATOM klass = 0;
if (klass == 0)
{
WNDCLASSW wndclass;
memset (&wndclass, 0, sizeof (wndclass));
wndclass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = _clutter_stage_win32_window_proc;
wndclass.cbWndExtra = sizeof (LONG_PTR);
wndclass.hInstance = GetModuleHandleW (NULL);
wndclass.hIcon = LoadIconW (NULL, (LPWSTR) IDI_APPLICATION);
wndclass.hCursor = LoadCursorW (NULL, (LPWSTR) IDC_ARROW);
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = L"ClutterStageWin32";
klass = RegisterClassW (&wndclass);
}
return klass;
}
static gboolean
clutter_stage_win32_check_gl_version ()
{
const char *version_string, *major_end, *minor_end;
int major = 0, minor = 0;
/* Get the OpenGL version number */
if ((version_string = (const char *) glGetString (GL_VERSION)) == NULL)
return FALSE;
/* Extract the major number */
for (major_end = version_string; *major_end >= '0'
&& *major_end <= '9'; major_end++)
major = (major * 10) + *major_end - '0';
/* If there were no digits or the major number isn't followed by a
dot then it is invalid */
if (major_end == version_string || *major_end != '.')
return FALSE;
/* Extract the minor number */
for (minor_end = major_end + 1; *minor_end >= '0'
&& *minor_end <= '9'; minor_end++)
minor = (minor * 10) + *minor_end - '0';
/* If there were no digits or there is an unexpected character then
it is invalid */
if (minor_end == major_end + 1
|| (*minor_end && *minor_end != ' ' && *minor_end != '.'))
return FALSE;
/* Accept OpenGL 1.2 or later */
return major > 1 || (major == 1 && minor >= 2);
}
static void
clutter_stage_win32_realize (ClutterActor *actor)
{
ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (actor);
PIXELFORMATDESCRIPTOR pfd;
int pf;
CLUTTER_NOTE (MISC, "Realizing main stage");
if (stage_win32->hwnd == NULL)
{
ATOM window_class = clutter_stage_win32_get_window_class ();
int win_xpos, win_ypos, win_width, win_height;
RECT win_rect;
POINT actual_pos;
if (window_class == 0)
{
g_critical ("Unable to register window class");
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return;
}
/* If we're in fullscreen mode then use the fullscreen rect
instead */
if ((stage_win32->state & CLUTTER_STAGE_STATE_FULLSCREEN))
{
get_fullscreen_rect (stage_win32);
win_xpos = stage_win32->fullscreen_rect.left;
win_ypos = stage_win32->fullscreen_rect.top;
win_width = stage_win32->fullscreen_rect.right - win_xpos;
win_height = stage_win32->fullscreen_rect.left - win_ypos;
}
else
{
if (stage_win32->win_xpos == 0 && stage_win32->win_ypos == 0)
win_xpos = win_ypos = CW_USEDEFAULT;
else
get_full_window_pos (stage_win32,
stage_win32->win_xpos, stage_win32->win_ypos,
&win_xpos, &win_ypos);
get_full_window_size (stage_win32,
stage_win32->win_width,
stage_win32->win_height,
&win_width, &win_height);
}
stage_win32->hwnd = CreateWindowW ((LPWSTR) MAKEINTATOM (window_class),
L".",
get_window_style (stage_win32),
win_xpos,
win_ypos,
win_width,
win_height,
NULL, NULL,
GetModuleHandle (NULL),
NULL);
if (stage_win32->hwnd == NULL)
{
g_critical ("Unable to create stage window");
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return;
}
/* Get the position in case CW_USEDEFAULT was specified */
if ((stage_win32->state & CLUTTER_STAGE_STATE_FULLSCREEN) == 0)
{
GetClientRect (stage_win32->hwnd, &win_rect);
actual_pos.x = win_rect.left;
actual_pos.y = win_rect.top;
ClientToScreen (stage_win32->hwnd, &actual_pos);
stage_win32->win_xpos = actual_pos.x;
stage_win32->win_ypos = actual_pos.y;
}
/* Store a pointer to the actor in the extra bytes of the window
so we can quickly access it in the window procedure */
SetWindowLongPtrW (stage_win32->hwnd, 0, (LONG_PTR) stage_win32);
}
if (stage_win32->gl_context)
wglDeleteContext (stage_win32->gl_context);
if (stage_win32->client_dc)
ReleaseDC (stage_win32->hwnd, stage_win32->client_dc);
stage_win32->client_dc = GetDC (stage_win32->hwnd);
memset (&pfd, 0, sizeof (pfd));
pfd.nSize = sizeof (pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cAlphaBits = 8;
pfd.cDepthBits = 32;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
if ((pf = ChoosePixelFormat (stage_win32->client_dc, &pfd)) == 0
|| !SetPixelFormat (stage_win32->client_dc, pf, &pfd))
{
g_critical ("Unable to find suitable GL pixel format");
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return;
}
stage_win32->gl_context = wglCreateContext (stage_win32->client_dc);
if (stage_win32->gl_context == NULL)
{
g_critical ("Unable to create suitable GL context");
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return;
}
CLUTTER_NOTE (GL, "wglMakeCurrent");
wglMakeCurrent (stage_win32->client_dc, stage_win32->gl_context);
if (!clutter_stage_win32_check_gl_version ())
{
g_critical ("OpenGL version number is too low");
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return;
}
/* Make sure the viewport gets set up correctly */
CLUTTER_SET_PRIVATE_FLAGS (actor, CLUTTER_ACTOR_SYNC_MATRICES);
}
static void
clutter_stage_win32_unrealize (ClutterActor *actor)
{
ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (actor);
wglMakeCurrent (NULL, NULL);
if (stage_win32->gl_context != NULL)
{
wglDeleteContext (stage_win32->gl_context);
stage_win32->gl_context = NULL;
}
if (stage_win32->client_dc)
{
ReleaseDC (stage_win32->hwnd, stage_win32->client_dc);
stage_win32->client_dc = NULL;
}
if (stage_win32->hwnd)
{
DestroyWindow (stage_win32->hwnd);
stage_win32->hwnd = NULL;
}
}
static void
clutter_stage_win32_dispose (GObject *gobject)
{
ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (gobject);
if (stage_win32->hwnd)
clutter_actor_unrealize (CLUTTER_ACTOR (gobject));
G_OBJECT_CLASS (clutter_stage_win32_parent_class)->dispose (gobject);
}
static void
clutter_stage_win32_class_init (ClutterStageWin32Class *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
ClutterStageClass *stage_class = CLUTTER_STAGE_CLASS (klass);
gobject_class->dispose = clutter_stage_win32_dispose;
actor_class->show = clutter_stage_win32_show;
actor_class->hide = clutter_stage_win32_hide;
actor_class->request_coords = clutter_stage_win32_request_coords;
actor_class->query_coords = clutter_stage_win32_query_coords;
actor_class->realize = clutter_stage_win32_realize;
actor_class->unrealize = clutter_stage_win32_unrealize;
stage_class->set_title = clutter_stage_win32_set_title;
stage_class->set_user_resize = clutter_stage_win32_set_user_resize;
stage_class->set_fullscreen = clutter_stage_win32_set_fullscreen;
}
static void
clutter_stage_win32_init (ClutterStageWin32 *stage)
{
stage->hwnd = NULL;
stage->client_dc = NULL;
stage->gl_context = NULL;
stage->win_xpos = 0;
stage->win_ypos = 0;
stage->win_width = 640;
stage->win_height = 480;
stage->backend = NULL;
stage->scroll_pos = 0;
}
/**
* clutter_win32_get_stage_window:
* @stage: a #ClutterStage
*
* Gets the stages window handle
*
* Return value: An HWND for the stage window.
*
* Since: 0.8
*/
HWND
clutter_win32_get_stage_window (ClutterStage *stage)
{
g_return_val_if_fail (CLUTTER_IS_STAGE_WIN32 (stage), NULL);
return CLUTTER_STAGE_WIN32 (stage)->hwnd;
}
void
clutter_stage_win32_map (ClutterStageWin32 *stage_win32)
{
CLUTTER_ACTOR_SET_FLAGS (stage_win32, CLUTTER_ACTOR_MAPPED);
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage_win32));
}
void
clutter_stage_win32_unmap (ClutterStageWin32 *stage_win32)
{
CLUTTER_ACTOR_UNSET_FLAGS (stage_win32, CLUTTER_ACTOR_MAPPED);
}

View File

@ -0,0 +1,84 @@
/* Clutter.
* An OpenGL based 'interactive canvas' library.
* Authored By Matthew Allum <mallum@openedhand.com>
* Copyright (C) 2006-2007 OpenedHand
*
* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CLUTTER_STAGE_WIN32_H__
#define __CLUTTER_STAGE_WIN32_H__
#include <glib-object.h>
#include <clutter/clutter-stage.h>
#include <windows.h>
#include "clutter-backend-win32.h"
G_BEGIN_DECLS
#define CLUTTER_TYPE_STAGE_WIN32 (clutter_stage_win32_get_type ())
#define CLUTTER_STAGE_WIN32(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_WIN32, ClutterStageWin32))
#define CLUTTER_IS_STAGE_WIN32(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE_WIN32))
#define CLUTTER_STAGE_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE_WIN32, ClutterStageWin32Class))
#define CLUTTER_IS_STAGE_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE_WIN32))
#define CLUTTER_STAGE_WIN32_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE_WIN32, ClutterStageWin32Class))
typedef struct _ClutterStageWin32 ClutterStageWin32;
typedef struct _ClutterStageWin32Class ClutterStageWin32Class;
struct _ClutterStageWin32
{
ClutterStage parent_instance;
HWND hwnd;
HDC client_dc;
HGLRC gl_context;
gint win_xpos;
gint win_ypos;
gint win_width;
gint win_height;
gint scroll_pos;
RECT fullscreen_rect;
ClutterBackendWin32 *backend;
ClutterStageState state;
};
struct _ClutterStageWin32Class
{
ClutterStageClass parent_class;
};
GType clutter_stage_win32_get_type (void) G_GNUC_CONST;
HWND clutter_win32_get_stage_window (ClutterStage *stage);
void clutter_stage_win32_map (ClutterStageWin32 *stage_win32);
void clutter_stage_win32_unmap (ClutterStageWin32 *stage_win32);
/* Defined in clutter-event-win32.c */
LRESULT CALLBACK _clutter_stage_win32_window_proc (HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam);
void _clutter_stage_win32_get_min_max_info (ClutterStageWin32 *stage_win32,
MINMAXINFO *min_max_info);
G_END_DECLS
#endif /* __CLUTTER_STAGE_H__ */

View File

@ -0,0 +1,39 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Authored By Matthew Allum <mallum@openedhand.com>
*
* Copyright (C) 2006 OpenedHand
*
* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CLUTTER_WIN32_H__
#define __CLUTTER_WIN32_H__
#include <glib.h>
#include <clutter/clutter-stage.h>
#include <windows.h>
G_BEGIN_DECLS
HWND clutter_win32_get_stage_window (ClutterStage *stage);
G_END_DECLS
#endif /* __CLUTTER_WIN32_H__ */

View File

@ -0,0 +1,14 @@
prefix=@prefix@
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
backend=@clutterbackend@
apiversion=@CLUTTER_API_VERSION@
requires=@CLUTTER_REQUIRES@
Name: Clutter
Description: Clutter Core Library (${backend} backend)
Version: @VERSION@
Libs: -L${libdir} -lclutter-${backend}-${apiversion}
Cflags: -I${includedir}/clutter-${apiversion}
Requires: ${requires}

View File

@ -273,7 +273,30 @@ case $clutterbackend in
[AC_MSG_ERROR([Unable to locate required GL headers])])
;;
*) AC_MSG_ERROR([Invalid backend for Clutter: use glx,sdl,osx,eglx or eglnative])
win32)
clutter_gl_header="GL/gl.h"
CLUTTER_FLAVOUR="win32"
AC_DEFINE([HAVE_CLUTTER_WIN32], 1, [Have the Win32 backend])
CLUTTER_COGL="gl"
AC_DEFINE([HAVE_COGL_GL], 1, [Have GL for rendering])
AC_CHECK_HEADERS([$clutter_gl_header],,
[AC_MSG_ERROR([Unable to locate required GL headers])])
dnl Use GLee under Windows instead of GL
AC_CHECK_LIB(GLee, GLeeInit, HAVE_LIBGLEE=yes, HAVE_LIBGLEE=no, -lopengl32)
if test "x$HAVE_LIBGLEE" = "xno"; then
AC_MSG_ERROR([libGLee not found]);
fi
WIN32_CFLAGS="-D_WIN32_WINNT=0x0500"
WIN32_LIBS="-lGLee -lopengl32 -lgdi32"
CLUTTER_LT_LDFLAGS="$CLUTTER_LT_LDFLAGS -no-undefined"
;;
*) AC_MSG_ERROR([Invalid backend for Clutter: use glx,sdl,osx,win32,eglx or eglnative])
;;
esac
@ -380,8 +403,8 @@ dnl ========================================================================
AC_SUBST(GCC_FLAGS)
CLUTTER_CFLAGS="$SDL_CFLAGS $EGL_CFLAGS $GLX_CFLAGS $OSX_CFLAGS $CLUTTER_DEPS_CFLAGS "
CLUTTER_LIBS="$SDL_LIBS $EGL_LIBS $GLX_LIBS $OSX_LIBS $CLUTTER_DEPS_LIBS"
CLUTTER_CFLAGS="$SDL_CFLAGS $EGL_CFLAGS $GLX_CFLAGS $OSX_CFLAGS $WIN32_CFLAGS $CLUTTER_DEPS_CFLAGS "
CLUTTER_LIBS="$SDL_LIBS $EGL_LIBS $GLX_LIBS $OSX_LIBS $WIN32_LIBS $CLUTTER_DEPS_LIBS"
AC_SUBST(CLUTTER_CFLAGS)
AC_SUBST(CLUTTER_LIBS)
@ -396,6 +419,8 @@ AC_CONFIG_FILES([
clutter/eglx/Makefile
clutter/eglnative/Makefile
clutter/osx/Makefile
clutter/win32/Makefile
clutter/win32/clutter-win32.pc
clutter/sdl/Makefile
clutter/cogl/Makefile
clutter/cogl/gl/Makefile