Mercurial > sdl-ios-xcode
view src/video/gtk/SDL_gtkvideo.c @ 4404:b8de86ee2ad6 SDL-1.2-olpc
First shot at GTK+ video target for the OLPC laptops. Seriously incomplete,
but it's enough to get some bits to a window...
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Thu, 19 Apr 2007 07:12:30 +0000 |
parents | |
children |
line wrap: on
line source
/* 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<SDL_arraysize(depth_list); ++i ) { if ( depth_list[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(); }