From 5e54859517ae6b47818098c0276ce4c6bf120cb8 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 19 Apr 2007 07:12:30 +0000 Subject: [PATCH] First shot at GTK+ video target for the OLPC laptops. Seriously incomplete, but it's enough to get some bits to a window... --- configure.in | 30 ++ include/SDL_config.h.in | 1 + src/video/SDL_sysvideo.h | 3 + src/video/SDL_video.c | 3 + src/video/gtk/SDL_gtkevents.c | 413 ++++++++++++++++++++ src/video/gtk/SDL_gtkevents_c.h | 34 ++ src/video/gtk/SDL_gtkmouse.c | 33 ++ src/video/gtk/SDL_gtkmouse_c.h | 26 ++ src/video/gtk/SDL_gtkvideo.c | 643 ++++++++++++++++++++++++++++++++ src/video/gtk/SDL_gtkvideo.h | 51 +++ 10 files changed, 1237 insertions(+) create mode 100644 src/video/gtk/SDL_gtkevents.c create mode 100644 src/video/gtk/SDL_gtkevents_c.h create mode 100644 src/video/gtk/SDL_gtkmouse.c create mode 100644 src/video/gtk/SDL_gtkmouse_c.h create mode 100644 src/video/gtk/SDL_gtkvideo.c create mode 100644 src/video/gtk/SDL_gtkvideo.h diff --git a/configure.in b/configure.in index 1d456a143..69099c3c2 100644 --- a/configure.in +++ b/configure.in @@ -1347,6 +1347,35 @@ AC_HELP_STRING([--enable-video-aalib], [use AAlib video driver [[default=no]]]), fi } +dnl Find the GTK+ includes +CheckGTK() +{ + AC_ARG_ENABLE(video-gtk, +AC_HELP_STRING([--enable-video-gtk], [use GTK+ video driver [[default=no]]]), + , enable_video_gtk=no) + if test x$enable_video = xyes -a x$enable_video_gtk = xyes; then + AC_PATH_PROG(HAVEPKGCONFIG, pkg-config, $PATH) + if test -z "$HAVEPKGCONFIG"; then + AC_MSG_ERROR([*** pkg-config not found. Cannot probe for gtk+-2.0.]) + else + AC_MSG_CHECKING(for GTK+ support) + video_gtk=no + $HAVEPKGCONFIG --exists gtk+-2.0 + if test $? -eq 0 ; then + video_gtk=yes + have_video=yes + AC_DEFINE(SDL_VIDEO_DRIVER_GTK) + SOURCES="$SOURCES $srcdir/src/video/gtk/*.c" + GTK2_CFLAGS=`$HAVEPKGCONFIG --cflags libglade-2.0 gtk+-2.0` + GTK2_LIBS=`$HAVEPKGCONFIG --libs libglade-2.0 gtk+-2.0` + EXTRA_CFLAGS="$EXTRA_CFLAGS $GTK2_CFLAGS" + EXTRA_LDFLAGS="$EXTRA_LDFLAGS $GTK2_LIBS" + fi + AC_MSG_RESULT($video_gtk) + fi + fi +} + dnl Set up the QTopia video driver if enabled CheckQtopia() { @@ -2161,6 +2190,7 @@ case "$host" in CheckVGL CheckWscons CheckAAlib + CheckGTK CheckQtopia CheckPicoGUI CheckOpenGLX11 diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in index afd6eb73f..decba8756 100644 --- a/include/SDL_config.h.in +++ b/include/SDL_config.h.in @@ -258,6 +258,7 @@ #undef SDL_VIDEO_DRIVER_GAPI #undef SDL_VIDEO_DRIVER_GEM #undef SDL_VIDEO_DRIVER_GGI +#undef SDL_VIDEO_DRIVER_GTK #undef SDL_VIDEO_DRIVER_IPOD #undef SDL_VIDEO_DRIVER_NANOX #undef SDL_VIDEO_DRIVER_OS2FS diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 80a579c35..75a2c0e50 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -401,6 +401,9 @@ extern VideoBootStrap OS2FSLib_bootstrap; #if SDL_VIDEO_DRIVER_AALIB extern VideoBootStrap AALIB_bootstrap; #endif +#if SDL_VIDEO_DRIVER_GTK +extern VideoBootStrap GTKPLUS_bootstrap; +#endif #if SDL_VIDEO_DRIVER_DUMMY extern VideoBootStrap DUMMY_bootstrap; #endif diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 3329b85ac..53f5eb609 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -117,6 +117,9 @@ static VideoBootStrap *bootstrap[] = { #if SDL_VIDEO_DRIVER_AALIB &AALIB_bootstrap, #endif +#if SDL_VIDEO_DRIVER_GTK + >KPLUS_bootstrap, +#endif #if SDL_VIDEO_DRIVER_DUMMY &DUMMY_bootstrap, #endif diff --git a/src/video/gtk/SDL_gtkevents.c b/src/video/gtk/SDL_gtkevents.c new file mode 100644 index 000000000..988234d86 --- /dev/null +++ b/src/video/gtk/SDL_gtkevents.c @@ -0,0 +1,413 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "SDL.h" +#include "../../events/SDL_sysevents.h" +#include "../../events/SDL_events_c.h" + +#include "SDL_gtkvideo.h" +#include "SDL_gtkevents_c.h" + +#define DEBUG_GTKPLUS_EVENTS 0 +#if DEBUG_GTKPLUS_EVENTS +#define DEBUG_GTKPLUS_SIGNAL() printf("GTK+: %s\n", __FUNCTION__) +#else +#define DEBUG_GTKPLUS_SIGNAL() +#endif + +static gboolean +signal_enter_notify(GtkWidget *w, GdkEventCrossing *evt, gpointer data) +{ + SET_THIS_POINTER(data); + DEBUG_GTKPLUS_SIGNAL(); + if ( (evt->mode != GDK_CROSSING_GRAB) && + (evt->mode != GDK_CROSSING_UNGRAB) ) { + if ( this->input_grab == SDL_GRAB_OFF ) { + SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS); + } + SDL_PrivateMouseMotion(0, 0, evt->x, evt->y); + } + + return FALSE; /* don't eat event, in case app connected a handler. */ +} + + +static gboolean +signal_leave_notify(GtkWidget *w, GdkEventCrossing *evt, gpointer data) +{ + SET_THIS_POINTER(data); + DEBUG_GTKPLUS_SIGNAL(); + if ( (evt->mode != GDK_CROSSING_GRAB) && + (evt->mode != GDK_CROSSING_UNGRAB) && + (evt->detail != GDK_NOTIFY_INFERIOR) ) { + if ( this->input_grab == SDL_GRAB_OFF ) { + SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS); + } else { + SDL_PrivateMouseMotion(0, 0, evt->x, evt->y); + } + } + return FALSE; /* don't eat event, in case app connected a handler. */ +} + + +static gboolean +signal_focus_in(GtkWidget *w, GdkEventFocus *evt, gpointer data) +{ + DEBUG_GTKPLUS_SIGNAL(); + SDL_PrivateAppActive(1, SDL_APPINPUTFOCUS); + return FALSE; /* don't eat event, in case app connected a handler. */ +} + + +static gboolean +signal_focus_out(GtkWidget *w, GdkEventFocus *evt, gpointer data) +{ + DEBUG_GTKPLUS_SIGNAL(); + SDL_PrivateAppActive(0, SDL_APPINPUTFOCUS); + return FALSE; /* don't eat event, in case app connected a handler. */ +} + + +#if 0 + /* Generated upon EnterWindow and FocusIn */ + case KeymapNotify: { +#ifdef DEBUG_XEVENTS +printf("KeymapNotify!\n"); +#endif + X11_SetKeyboardState(SDL_Display, xevent.xkeymap.key_vector); + } + break; +#endif + + +static gboolean +signal_motion_notify(GtkWidget *w, GdkEventMotion *evt, gpointer data) +{ + SET_THIS_POINTER(data); + DEBUG_GTKPLUS_SIGNAL(); + if ( SDL_VideoSurface ) { + if ( 0 /* !!! FIXME mouse_relative */ ) { + SDLGTK_WarpedMotion(this, evt); + } else { + SDL_PrivateMouseMotion(0, 0, evt->x, evt->y); + } + } + + return FALSE; /* don't eat event, in case app connected a handler. */ +} + + +static gboolean +signal_button_press(GtkWidget *w, GdkEventButton *evt, gpointer data) +{ + DEBUG_GTKPLUS_SIGNAL(); + SDL_PrivateMouseButton(SDL_PRESSED, evt->button, 0, 0); + return FALSE; /* don't eat event, in case app connected a handler. */ +} + +static gboolean +signal_button_release(GtkWidget *w, GdkEventButton *evt, gpointer data) +{ + DEBUG_GTKPLUS_SIGNAL(); + SDL_PrivateMouseButton(SDL_RELEASED, evt->button, 0, 0); + return FALSE; /* don't eat event, in case app connected a handler. */ +} + +static gboolean +signal_expose(GtkWidget *w, GdkEventExpose *evt, gpointer data) +{ + SET_THIS_POINTER(data); + const GdkRectangle *area = &evt->area; + SDL_Rect r = { area->x, area->y, area->width, area->height }; + DEBUG_GTKPLUS_SIGNAL(); + this->UpdateRects(this, 1, &r); + return FALSE; /* don't eat event, in case app connected a handler. */ +} + + +static gboolean +signal_delete(GtkWidget *w, GdkEvent *evt, gpointer data) +{ + DEBUG_GTKPLUS_SIGNAL(); + SDL_PrivateQuit(); + return TRUE; /* eat event: default handler destroys window! */ +} + + +void GTKPLUS_ConnectSignals(_THIS) +{ + gtk_widget_set_events( this->hidden->gtkdrawingarea, + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); + + gtk_signal_connect(GTK_OBJECT(this->hidden->gtkwindow), + "delete-event", + GTK_SIGNAL_FUNC(signal_delete), this); + gtk_signal_connect(GTK_OBJECT(this->hidden->gtkdrawingarea), + "enter-notify-event", + GTK_SIGNAL_FUNC(signal_enter_notify), this); + gtk_signal_connect(GTK_OBJECT(this->hidden->gtkdrawingarea), + "leave-notify-event", + GTK_SIGNAL_FUNC(signal_leave_notify), this); + gtk_signal_connect(GTK_OBJECT(this->hidden->gtkwindow), + "focus-in-event", + GTK_SIGNAL_FUNC(signal_focus_in), this); + gtk_signal_connect(GTK_OBJECT(this->hidden->gtkwindow), + "focus-out-event", + GTK_SIGNAL_FUNC(signal_focus_out), this); + gtk_signal_connect(GTK_OBJECT(this->hidden->gtkdrawingarea), + "motion-notify-event", + GTK_SIGNAL_FUNC(signal_motion_notify), this); + gtk_signal_connect(GTK_OBJECT(this->hidden->gtkdrawingarea), + "button-press-event", + GTK_SIGNAL_FUNC(signal_button_press), this); + gtk_signal_connect(GTK_OBJECT(this->hidden->gtkdrawingarea), + "button-release-event", + GTK_SIGNAL_FUNC(signal_button_release), this); + gtk_signal_connect(GTK_OBJECT(this->hidden->gtkdrawingarea), + "expose-event", GTK_SIGNAL_FUNC(signal_expose), this); + /* !!! FIXME: more to connect here. */ +} + + + +/* !!! FIXME: more to connect here. */ +#if 0 + /* Key press? */ + case KeyPress: { + static SDL_keysym saved_keysym; + SDL_keysym keysym; + KeyCode keycode = xevent.xkey.keycode; + +#ifdef DEBUG_XEVENTS +printf("KeyPress (X11 keycode = 0x%X)\n", xevent.xkey.keycode); +#endif + /* Get the translated SDL virtual keysym */ + if ( keycode ) { + keysym.scancode = keycode; + keysym.sym = X11_TranslateKeycode(SDL_Display, keycode); + keysym.mod = KMOD_NONE; + keysym.unicode = 0; + } else { + keysym = saved_keysym; + } + + /* If we're not doing translation, we're done! */ + if ( !SDL_TranslateUNICODE ) { + posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym); + break; + } + + if ( XFilterEvent(&xevent, None) ) { + if ( xevent.xkey.keycode ) { + posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym); + } else { + /* Save event to be associated with IM text + In 1.3 we'll have a text event instead.. */ + saved_keysym = keysym; + } + break; + } + + /* Look up the translated value for the key event */ +#ifdef X_HAVE_UTF8_STRING + if ( SDL_IC != NULL ) { + static Status state; + /* A UTF-8 character can be at most 6 bytes */ + char keybuf[6]; + if ( Xutf8LookupString(SDL_IC, &xevent.xkey, + keybuf, sizeof(keybuf), + NULL, &state) ) { + keysym.unicode = Utf8ToUcs4((Uint8*)keybuf); + } + } + else +#endif + { + static XComposeStatus state; + char keybuf[32]; + + if ( XLookupString(&xevent.xkey, + keybuf, sizeof(keybuf), + NULL, &state) ) { + /* + * FIXME: XLookupString() may yield more than one + * character, so we need a mechanism to allow for + * this (perhaps null keypress events with a + * unicode value) + */ + keysym.unicode = (Uint8)keybuf[0]; + } + } + posted = SDL_PrivateKeyboard(SDL_PRESSED, &keysym); + } + break; + + /* Key release? */ + case KeyRelease: { + SDL_keysym keysym; + KeyCode keycode = xevent.xkey.keycode; + +#ifdef DEBUG_XEVENTS +printf("KeyRelease (X11 keycode = 0x%X)\n", xevent.xkey.keycode); +#endif + /* Check to see if this is a repeated key */ + if ( X11_KeyRepeat(SDL_Display, &xevent) ) { + break; + } + + /* Get the translated SDL virtual keysym */ + keysym.scancode = keycode; + keysym.sym = X11_TranslateKeycode(SDL_Display, keycode); + keysym.mod = KMOD_NONE; + keysym.unicode = 0; + + posted = SDL_PrivateKeyboard(SDL_RELEASED, &keysym); + } + break; + + /* Have we been iconified? */ + case UnmapNotify: { +#ifdef DEBUG_XEVENTS +printf("UnmapNotify!\n"); +#endif + /* If we're active, make ourselves inactive */ + if ( SDL_GetAppState() & SDL_APPACTIVE ) { + /* Swap out the gamma before we go inactive */ + X11_SwapVidModeGamma(this); + + /* Send an internal deactivate event */ + posted = SDL_PrivateAppActive(0, + SDL_APPACTIVE|SDL_APPINPUTFOCUS); + } + } + break; + + /* Have we been restored? */ + case MapNotify: { +#ifdef DEBUG_XEVENTS +printf("MapNotify!\n"); +#endif + /* If we're not active, make ourselves active */ + if ( !(SDL_GetAppState() & SDL_APPACTIVE) ) { + /* Send an internal activate event */ + posted = SDL_PrivateAppActive(1, SDL_APPACTIVE); + + /* Now that we're active, swap the gamma back */ + X11_SwapVidModeGamma(this); + } + + if ( SDL_VideoSurface && + (SDL_VideoSurface->flags & SDL_FULLSCREEN) ) { + X11_EnterFullScreen(this); + } else { + X11_GrabInputNoLock(this, this->input_grab); + } + X11_CheckMouseModeNoLock(this); + + if ( SDL_VideoSurface ) { + X11_RefreshDisplay(this); + } + } + break; + + /* Have we been resized or moved? */ + case ConfigureNotify: { +#ifdef DEBUG_XEVENTS +printf("ConfigureNotify! (resize: %dx%d)\n", xevent.xconfigure.width, xevent.xconfigure.height); +#endif + if ( SDL_VideoSurface ) { + if ((xevent.xconfigure.width != SDL_VideoSurface->w) || + (xevent.xconfigure.height != SDL_VideoSurface->h)) { + /* FIXME: Find a better fix for the bug with KDE 1.2 */ + if ( ! ((xevent.xconfigure.width == 32) && + (xevent.xconfigure.height == 32)) ) { + SDL_PrivateResize(xevent.xconfigure.width, + xevent.xconfigure.height); + } + } else { + /* OpenGL windows need to know about the change */ + if ( SDL_VideoSurface->flags & SDL_OPENGL ) { + SDL_PrivateExpose(); + } + } + } + } + break; + + /* Have we been requested to quit (or another client message?) */ + case ClientMessage: { + if ( (xevent.xclient.format == 32) && + (xevent.xclient.data.l[0] == WM_DELETE_WINDOW) ) + { + posted = SDL_PrivateQuit(); + } else + if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) { + SDL_SysWMmsg wmmsg; + + SDL_VERSION(&wmmsg.version); + wmmsg.subsystem = SDL_SYSWM_X11; + wmmsg.event.xevent = xevent; + posted = SDL_PrivateSysWMEvent(&wmmsg); + } + } + break; + + + + default: { +#ifdef DEBUG_XEVENTS +printf("Unhandled event %d\n", xevent.type); +#endif + /* Only post the event if we're watching for it */ + if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) { + SDL_SysWMmsg wmmsg; + + SDL_VERSION(&wmmsg.version); + wmmsg.subsystem = SDL_SYSWM_X11; + wmmsg.event.xevent = xevent; + posted = SDL_PrivateSysWMEvent(&wmmsg); + } + } + break; + } +#endif + + + +void GTKPLUS_PumpEvents(_THIS) +{ + while (gtk_events_pending()) + gtk_main_iteration(); +} + +void GTKPLUS_InitOSKeymap(_THIS) +{ + /* !!! FIXME */ + /* do nothing. */ +} + +/* end of SDL_gtkevents.c ... */ + diff --git a/src/video/gtk/SDL_gtkevents_c.h b/src/video/gtk/SDL_gtkevents_c.h new file mode 100644 index 000000000..c458ee3e1 --- /dev/null +++ b/src/video/gtk/SDL_gtkevents_c.h @@ -0,0 +1,34 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "SDL_gtkvideo.h" + +/* Variables and functions exported by SDL_sysevents.c to other parts + of the native video subsystem (SDL_sysvideo.c) +*/ +extern void GTKPLUS_InitOSKeymap(_THIS); +extern void GTKPLUS_PumpEvents(_THIS); +extern void GTKPLUS_ConnectSignals(_THIS); + +/* end of SDL_gtkevents_c.h ... */ + diff --git a/src/video/gtk/SDL_gtkmouse.c b/src/video/gtk/SDL_gtkmouse.c new file mode 100644 index 000000000..0d03e7189 --- /dev/null +++ b/src/video/gtk/SDL_gtkmouse.c @@ -0,0 +1,33 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "SDL_mouse.h" +#include "../../events/SDL_events_c.h" + +#include "SDL_gtkmouse_c.h" + + +/* The implementation dependent data for the window manager cursor */ +struct WMcursor { + int unused; +}; diff --git a/src/video/gtk/SDL_gtkmouse_c.h b/src/video/gtk/SDL_gtkmouse_c.h new file mode 100644 index 000000000..04fece674 --- /dev/null +++ b/src/video/gtk/SDL_gtkmouse_c.h @@ -0,0 +1,26 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "SDL_gtkvideo.h" + +/* Functions to be exported */ diff --git a/src/video/gtk/SDL_gtkvideo.c b/src/video/gtk/SDL_gtkvideo.c new file mode 100644 index 000000000..bf32689bb --- /dev/null +++ b/src/video/gtk/SDL_gtkvideo.c @@ -0,0 +1,643 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* + * GTK+ SDL video driver implementation; this is a little like a pared-down + * version of the X11 driver. You almost certainly do NOT want this target + * on a desktop machine. This was written for the One Laptop Per Child + * project so they wouldn't need to use the SDL_WINDOWID hack with the X11 + * driver and compete for the event queue. + * + * Initial work by Ryan C. Gordon (icculus@icculus.org). A good portion + * of this was cut-and-pasted from the dummy video target just to have a + * starting point for the bare minimum to fill in, and some was lifted from + * the x11 target. + */ + +#include "SDL_video.h" +#include "SDL_mouse.h" +#include "../SDL_sysvideo.h" +#include "../SDL_pixels_c.h" +#include "../../events/SDL_events_c.h" + +#include "SDL_gtkvideo.h" +#include "SDL_gtkevents_c.h" +#include "SDL_gtkmouse_c.h" + +#define GTKPLUSVID_DRIVER_NAME "gtk" + +/* Initialization/Query functions */ +static int GTKPLUS_VideoInit(_THIS, SDL_PixelFormat *vformat); +static SDL_Rect **GTKPLUS_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags); +static SDL_Surface *GTKPLUS_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags); +static int GTKPLUS_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors); +static void GTKPLUS_VideoQuit(_THIS); + +/* Hardware surface functions */ +static int GTKPLUS_AllocHWSurface(_THIS, SDL_Surface *surface); +static int GTKPLUS_LockHWSurface(_THIS, SDL_Surface *surface); +static void GTKPLUS_UnlockHWSurface(_THIS, SDL_Surface *surface); +static void GTKPLUS_FreeHWSurface(_THIS, SDL_Surface *surface); +static void GTKPLUS_SetCaption(_THIS, const char *title, const char *icon); + +/* etc. */ +static void GTKPLUS_UpdateRects(_THIS, int numrects, SDL_Rect *rects); + +/* GTKPLUS driver bootstrap functions */ + +static int do_gtk_init(void) +{ + static int initted = 0; + if (!initted) { /* !!! FIXME: I can't see a way to deinit gtk... */ + int tmpargc = 0; + char *args[] = { NULL, NULL }; + char **tmpargv = args; + initted = (gtk_init_check(&tmpargc, &tmpargv)); + } + return initted; +} + + +static int GTKPLUS_Available(void) +{ + return 1; /* !!! FIXME */ +} + +static void GTKPLUS_DeleteDevice(SDL_VideoDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_VideoDevice *GTKPLUS_CreateDevice(int devindex) +{ + SDL_VideoDevice *device; + + /* Initialize all variables that we clean on shutdown */ + device = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice)); + if ( device ) { + SDL_memset(device, 0, (sizeof *device)); + device->hidden = (struct SDL_PrivateVideoData *) + SDL_malloc((sizeof *device->hidden)); + } + if ( (device == NULL) || (device->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( device ) { + SDL_free(device); + } + return(0); + } + SDL_memset(device->hidden, 0, (sizeof *device->hidden)); + + device->handles_any_size = 1; + + /* Set the function pointers */ + device->VideoInit = GTKPLUS_VideoInit; + device->ListModes = GTKPLUS_ListModes; + device->SetVideoMode = GTKPLUS_SetVideoMode; + device->CreateYUVOverlay = NULL; + device->SetColors = GTKPLUS_SetColors; + device->UpdateRects = GTKPLUS_UpdateRects; + device->VideoQuit = GTKPLUS_VideoQuit; + device->AllocHWSurface = GTKPLUS_AllocHWSurface; + device->CheckHWBlit = NULL; + device->FillHWRect = NULL; + device->SetHWColorKey = NULL; + device->SetHWAlpha = NULL; + device->LockHWSurface = GTKPLUS_LockHWSurface; + device->UnlockHWSurface = GTKPLUS_UnlockHWSurface; + device->FlipHWSurface = NULL; + device->FreeHWSurface = GTKPLUS_FreeHWSurface; + device->SetCaption = GTKPLUS_SetCaption; + device->SetIcon = NULL; + device->IconifyWindow = NULL; + device->GrabInput = NULL; + device->GetWMInfo = NULL; + device->InitOSKeymap = GTKPLUS_InitOSKeymap; + device->PumpEvents = GTKPLUS_PumpEvents; + + device->free = GTKPLUS_DeleteDevice; + + return device; +} + +VideoBootStrap GTKPLUS_bootstrap = { + GTKPLUSVID_DRIVER_NAME, "SDL GTK+ video driver", + GTKPLUS_Available, GTKPLUS_CreateDevice +}; + + +static int add_visual(_THIS, int depth, GdkVisualType vistype) +{ + GdkVisual *vi = gdk_visual_get_best_with_both(depth, vistype); + if(vi != NULL) { + g_object_ref(vi); + this->hidden->visuals[this->hidden->nvisuals++] = vi; + } + return(this->hidden->nvisuals); +} + +static int GTKPLUS_GetVideoModes(_THIS) +{ + const gint screen_w = gdk_screen_width(); + const gint screen_h = gdk_screen_height(); + int i, n; + + { + /* It's interesting to note that if we allow 32 bit depths (on X11), + we get a visual with an alpha mask on composite servers. + static int depth_list[] = { 32, 24, 16, 15, 8 }; + */ + static int depth_list[] = { 24, 16, 15, 8 }; + int use_directcolor = 1; + + /* Search for the visuals in deepest-first order, so that the first + will be the richest one */ + if ( SDL_getenv("SDL_VIDEO_GTK_NODIRECTCOLOR") ) { + use_directcolor = 0; + } + this->hidden->nvisuals = 0; + for ( i=0; i 8 ) { + if ( use_directcolor ) { + add_visual(this, depth_list[i], GDK_VISUAL_DIRECT_COLOR); + } + add_visual(this, depth_list[i], GDK_VISUAL_TRUE_COLOR); + } else { + add_visual(this, depth_list[i], GDK_VISUAL_PSEUDO_COLOR); + add_visual(this, depth_list[i], GDK_VISUAL_STATIC_COLOR); + } + } + if ( this->hidden->nvisuals == 0 ) { + SDL_SetError("Found no sufficiently capable GTK+ visuals"); + return -1; + } + } + + return 0; +} + + +int GTKPLUS_VideoInit(_THIS, SDL_PixelFormat *vformat) +{ + GdkVisual *sysvis = NULL; + int i; + + if (!do_gtk_init()) { + return -1; + } + + /* Get the available video modes */ + if(GTKPLUS_GetVideoModes(this) < 0) + return -1; + + /* Determine the current screen size */ + this->info.current_w = gdk_screen_width(); + this->info.current_h = gdk_screen_height(); + + /* Determine the default screen depth: + Use the default visual (or at least one with the same depth) */ + this->hidden->display_colormap = gdk_colormap_get_system(); /* !!! FIXME: refcount? */ + sysvis = gdk_visual_get_system(); /* !!! FIXME: refcount? */ + + for(i = 0; i < this->hidden->nvisuals; i++) { + if(this->hidden->visuals[i]->depth == sysvis->depth) + break; + } + + if(i == this->hidden->nvisuals) { + /* default visual was useless, take the deepest one instead */ + i = 0; + } + + this->hidden->visual = this->hidden->visuals[i]; + if ( this->hidden->visual == sysvis ) { /* !!! FIXME: same pointer? */ + this->hidden->colormap = this->hidden->display_colormap; + g_object_ref(this->hidden->colormap); + } else { + this->hidden->colormap = gdk_colormap_new(this->hidden->visual, FALSE); + } + + // !!! FIXME: this is not a public GDK symbol!! + vformat->BitsPerPixel = _gdk_windowing_get_bits_for_depth( + gdk_display_get_default(), + this->hidden->visuals[i]->depth); + this->hidden->depth = vformat->BitsPerPixel; + + if ( vformat->BitsPerPixel > 8 ) { + vformat->Rmask = this->hidden->visual->red_mask; + vformat->Gmask = this->hidden->visual->green_mask; + vformat->Bmask = this->hidden->visual->blue_mask; + } + if ( this->hidden->depth == 32 ) { + vformat->Amask = (0xFFFFFFFF & ~(vformat->Rmask|vformat->Gmask|vformat->Bmask)); + } + +#if 0 + /* Create the fullscreen and managed windows */ + create_aux_windows(this); + + /* Create the blank cursor */ + SDL_BlankCursor = this->CreateWMCursor(this, blank_cdata, blank_cmask, + BLANK_CWIDTH, BLANK_CHEIGHT, + BLANK_CHOTX, BLANK_CHOTY); + + /* Allow environment override of screensaver disable. */ + env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER"); + this->hidden->allow_screensaver = ( (env && SDL_atoi(env)) ? 1 : 0 ); +#endif + + /* We're done! */ + gdk_flush(); /* just in case. */ + + /* Fill in some window manager capabilities */ + this->info.wm_available = 1; + + return(0); +} + + +static GdkVisual *find_visual(_THIS, int bpp) +{ + GdkDisplay *display = gdk_display_get_default(); + int i; + for ( i = 0; i < this->hidden->nvisuals; i++ ) { + const int videpth = this->hidden->visuals[i]->depth; + // !!! FIXME: this is not a public GDK symbol!! + const int depth = _gdk_windowing_get_bits_for_depth(display, videpth); + if ( depth == bpp ) + break; + } + + if ( i == this->hidden->nvisuals ) { + SDL_SetError("No matching visual for requested depth"); + return NULL; /* should never happen */ + } + return this->hidden->visuals[i]; +} + + +SDL_Rect **GTKPLUS_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags) +{ + if ((flags & SDL_OPENGL) == 0) { + if (find_visual(this, format->BitsPerPixel) != NULL) { + return (SDL_Rect **) -1; /* !!! FIXME: maybe not right. */ + } + } + return NULL; /* unsupported. */ +} + + +SDL_Surface *GTKPLUS_SetVideoMode(_THIS, SDL_Surface *current, + int width, int height, int bpp, Uint32 flags) +{ + Uint32 Amask = 0; + int vis_change = 0; + GtkWindow *win = NULL; + GdkImage *img = NULL; + GdkVisual *sysvis = gdk_visual_get_system(); /* !!! FIXME: refcount? */ + GdkVisual *vis = find_visual(this, bpp); + if (vis == NULL) { + return(NULL); + } + + if (flags & SDL_OPENGL) { + SDL_SetError("No OpenGL support in the GTK+ target"); + return(NULL); + } + + /* These are the only flags we allow here... */ + flags &= /*SDL_FULLSCREEN |*/ SDL_RESIZABLE | SDL_NOFRAME | SDL_HWPALETTE; + + vis_change = (vis != this->hidden->visual); + this->hidden->visual = vis; + this->hidden->depth = vis->depth; + + /* Allocate the new pixel format for this video mode */ + if ( this->hidden->depth == 32 ) { + Amask = (0xFFFFFFFF & ~(vis->red_mask|vis->green_mask|vis->blue_mask)); + } else { + Amask = 0; + } + if ( ! SDL_ReallocFormat(current, bpp, + vis->red_mask, vis->green_mask, vis->blue_mask, Amask) ) { + return NULL; + } + + /* Create the appropriate colormap */ + g_object_unref(this->hidden->colormap); + this->hidden->colormap = NULL; + + if ( this->hidden->visual->type == GDK_VISUAL_PSEUDO_COLOR ) { + int ncolors; + + /* Allocate the pixel flags */ + ncolors = this->hidden->visual->colormap_size; + + #if 0 + SDL_XPixels = SDL_malloc(ncolors * sizeof(int)); + if(SDL_XPixels == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_memset(SDL_XPixels, 0, ncolors * sizeof(*SDL_XPixels)); + #endif + + /* always allocate a private colormap on non-default visuals */ + if ( this->hidden->visual != sysvis ) { + flags |= SDL_HWPALETTE; + } + if ( flags & SDL_HWPALETTE ) { + current->flags |= SDL_HWPALETTE; + this->hidden->colormap = gdk_colormap_new(this->hidden->visual, TRUE); + } else { + this->hidden->colormap = this->hidden->display_colormap; + g_object_ref(this->hidden->colormap); + } + } else if ( this->hidden->visual->type == GDK_VISUAL_DIRECT_COLOR ) { + + /* Create a colormap which we can manipulate for gamma */ + this->hidden->colormap = gdk_colormap_new(this->hidden->visual, TRUE); + gdk_flush(); + + /* Initialize the colormap to the identity mapping */ + SDL_GetGammaRamp(0, 0, 0); + this->screen = current; +#if 0 // !!! FIXME + GTKPLUS_SetGammaRamp(this, this->gamma); +#endif + this->screen = NULL; + } else { + /* Create a read-only colormap for our window */ + this->hidden->colormap = gdk_colormap_new(this->hidden->visual, FALSE); + } + +#if 0 // !!! FIXME + /* Recreate the auxiliary windows, if needed (required for GL) */ + if ( vis_change ) + create_aux_windows(this); + + if(current->flags & SDL_HWPALETTE) { + /* Since the full-screen window might have got a nonzero background + colour (0 is white on some displays), we should reset the + background to 0 here since that is what the user expects + with a private colormap */ + XSetWindowBackground(SDL_Display, FSwindow, 0); + XClearWindow(SDL_Display, FSwindow); + } + + /* resize the (possibly new) window manager window */ + if( !SDL_windowid ) { + X11_SetSizeHints(this, w, h, flags); + window_w = w; + window_h = h; + XResizeWindow(SDL_Display, WMwindow, w, h); + } +#endif + + if ( this->hidden->gdkimage ) { + g_object_unref(this->hidden->gdkimage); + this->hidden->gdkimage = NULL; + } + + img = this->hidden->gdkimage = gdk_image_new(GDK_IMAGE_FASTEST, + vis, width, height); + if (img == NULL) { + SDL_SetError("Couldn't allocate buffer for requested mode"); + return(NULL); + } + gdk_image_set_colormap(this->hidden->gdkimage, this->hidden->colormap); + + SDL_memset(img->mem, 0, height * img->bpl); + + if ( this->hidden->gtkwindow == NULL ) { + this->hidden->gtkdrawingarea = gtk_drawing_area_new(); + if ( this->hidden->gtkdrawingarea == NULL ) { + SDL_SetError("Couldn't create drawing area for requested mode"); + g_object_unref(this->hidden->gdkimage); + this->hidden->gdkimage = NULL; + return(NULL); + } + + this->hidden->gtkwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); + if ( this->hidden->gtkwindow == NULL ) { + SDL_SetError("Couldn't create window for requested mode"); + g_object_unref(this->hidden->gdkimage); + g_object_unref(this->hidden->gtkdrawingarea); + this->hidden->gdkimage = NULL; + this->hidden->gtkdrawingarea = NULL; + return(NULL); + } + + gtk_window_set_title(GTK_WINDOW(this->hidden->gtkwindow), ""); + gtk_widget_set_app_paintable(this->hidden->gtkwindow, TRUE); + gtk_widget_set_app_paintable(this->hidden->gtkdrawingarea, TRUE); + gtk_widget_set_double_buffered(this->hidden->gtkwindow, FALSE); + gtk_widget_set_double_buffered(this->hidden->gtkdrawingarea, FALSE); + + GTKPLUS_ConnectSignals(this); + + gtk_container_add(GTK_CONTAINER(this->hidden->gtkwindow), + this->hidden->gtkdrawingarea); + } + + win = GTK_WINDOW(this->hidden->gtkwindow); + gtk_widget_set_colormap(this->hidden->gtkdrawingarea, this->hidden->colormap); + +// !!! FIXME +#if 0 + /* Cache the window in the server, when possible */ + { + Screen *xscreen; + XSetWindowAttributes a; + + xscreen = ScreenOfDisplay(SDL_Display, SDL_Screen); + a.backing_store = DoesBackingStore(xscreen); + if ( a.backing_store != NotUseful ) { + XChangeWindowAttributes(SDL_Display, SDL_Window, + CWBackingStore, &a); + } + } + + /* Update the internal keyboard state */ + X11_SetKeyboardState(SDL_Display, NULL); + + /* When the window is first mapped, ignore non-modifier keys */ + { + Uint8 *keys = SDL_GetKeyState(NULL); + for ( i = 0; i < SDLK_LAST; ++i ) { + switch (i) { + case SDLK_NUMLOCK: + case SDLK_CAPSLOCK: + case SDLK_LCTRL: + case SDLK_RCTRL: + case SDLK_LSHIFT: + case SDLK_RSHIFT: + case SDLK_LALT: + case SDLK_RALT: + case SDLK_LMETA: + case SDLK_RMETA: + case SDLK_MODE: + break; + default: + keys[i] = SDL_RELEASED; + break; + } + } + } + + /* Map them both and go fullscreen, if requested */ + if ( ! SDL_windowid ) { + XMapWindow(SDL_Display, SDL_Window); + XMapWindow(SDL_Display, WMwindow); + X11_WaitMapped(this, WMwindow); + if ( flags & SDL_FULLSCREEN ) { + current->flags |= SDL_FULLSCREEN; + X11_EnterFullScreen(this); + } else { + current->flags &= ~SDL_FULLSCREEN; + } + } +#endif + + if ((flags & SDL_FULLSCREEN) == 0) { + gtk_window_unfullscreen(win); + } else { + gtk_window_fullscreen(win); + flags &= ~SDL_RESIZABLE; + flags |= SDL_NOFRAME; + } + + gtk_window_set_resizable(win, (flags & SDL_RESIZABLE) ? TRUE : FALSE); + gtk_window_set_decorated(win, (flags & SDL_NOFRAME) ? FALSE : TRUE); + gtk_window_resize(win, width, height); + gtk_widget_set_size_request(this->hidden->gtkdrawingarea, width, height); + gtk_widget_show(this->hidden->gtkdrawingarea); + gtk_widget_show(this->hidden->gtkwindow); + + /* Set up the new mode framebuffer */ + current->w = width; + current->h = height; + //current->format->depth = vis->bits_per_rgb; + current->flags = flags | SDL_PREALLOC; + current->pitch = img->bpl; + current->pixels = this->hidden->gdkimage->mem; + + /* We're done */ + return(current); +} + +static void GTKPLUS_SetCaption(_THIS, const char *title, const char *icon) +{ + gtk_window_set_title(GTK_WINDOW(this->hidden->gtkwindow), + (const gchar *) title); +} + +/* We don't actually allow hardware surfaces. */ +static int GTKPLUS_AllocHWSurface(_THIS, SDL_Surface *surface) +{ + return(-1); +} + +static void GTKPLUS_FreeHWSurface(_THIS, SDL_Surface *surface) +{ +} + +/* We need to wait for vertical retrace on page flipped displays */ +static int GTKPLUS_LockHWSurface(_THIS, SDL_Surface *surface) +{ + return(0); +} + +static void GTKPLUS_UnlockHWSurface(_THIS, SDL_Surface *surface) +{ +} + +static void GTKPLUS_UpdateRects(_THIS, int numrects, SDL_Rect *rects) +{ + if ( (this->hidden->gtkdrawingarea != NULL) && + (GTK_WIDGET_DRAWABLE(this->hidden->gtkdrawingarea)) && + (numrects > 0) ) { + GdkDrawable *draw = GDK_DRAWABLE(this->hidden->gtkdrawingarea->window); + if (this->hidden->gc == NULL) { + this->hidden->gc = gdk_gc_new(draw); + } + + if (this->hidden->gc != NULL) { + GdkImage *img = this->hidden->gdkimage; + const SDL_Rect *r = rects; + int i; + for (i = 0; i < numrects; i++, r++) { + const gint x = r->x; + const gint y = r->y; + gdk_draw_image(draw, this->hidden->gc, img, + x, y, x, y, r->w, r->h); + } + gdk_flush(); /* transfer the GdkImage so we can make changes. */ + } + } +} + +int GTKPLUS_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) +{ + /* !!! FIXME */ + return(0); +} + +/* Note: If we are terminated, this could be called in the middle of + another SDL video routine -- notably UpdateRects. +*/ +void GTKPLUS_VideoQuit(_THIS) +{ + int i; + + gdk_flush(); + + if (this->hidden->gc != NULL) { + g_object_unref(this->hidden->gc); + this->hidden->gc = NULL; + } + + if ( this->hidden->gtkwindow ) { + /* this deletes the drawing area widget, too. */ + gtk_widget_destroy(this->hidden->gtkwindow); + this->hidden->gtkwindow = NULL; + } + + if ( this->hidden->gdkimage ) { + g_object_unref(this->hidden->gdkimage); + this->hidden->gdkimage = NULL; + } + + for (i = 0; i < this->hidden->nvisuals; i++) { + g_object_unref(this->hidden->visuals[i]); + this->hidden->visuals[i] = NULL; + } + this->hidden->nvisuals = 0; + + g_object_unref(this->hidden->colormap); + this->hidden->colormap = NULL; + + gdk_flush(); +} + diff --git a/src/video/gtk/SDL_gtkvideo.h b/src/video/gtk/SDL_gtkvideo.h new file mode 100644 index 000000000..06a6b3410 --- /dev/null +++ b/src/video/gtk/SDL_gtkvideo.h @@ -0,0 +1,51 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_gtkvideo_h +#define _SDL_gtkvideo_h + +#include "../SDL_sysvideo.h" + +#include + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_VideoDevice *this +#define SET_THIS_POINTER(x) SDL_VideoDevice *this = (SDL_VideoDevice *) x + + +/* Private display data */ + +struct SDL_PrivateVideoData { + GdkImage *gdkimage; + GtkWidget *gtkwindow; + GtkWidget *gtkdrawingarea; + GdkVisual *visuals[2*5]; /* at most 2 entries for 8, 15, 16, 24, 32 */ + GdkVisual *visual; + GdkColormap *colormap; + GdkColormap *display_colormap; + GdkGC *gc; + int nvisuals; + int depth; +}; + +#endif /* _SDL_gtkvideo_h */