/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* ** GLW_IMP.C ** ** This file contains ALL Linux specific stuff having to do with the ** OpenGL refresh. When a port is being made the following functions ** must be implemented by the port: ** ** GLimp_EndFrame ** GLimp_Init ** GLimp_Shutdown ** GLimp_SwitchFullscreen ** */ #include #include #include #include #include #include #include #include #include "x11.h" #include "../ref_gl/r_local.h" #include "../client/keys.h" #include "glw_linux.h" #define DISPLAY_MASK (VisibilityChangeMask | StructureNotifyMask | ExposureMask ) #define INIT_MASK (KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | DISPLAY_MASK ) #define KEY_MASK (KeyPressMask | KeyReleaseMask) #define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ButtonMotionMask ) #define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | StructureNotifyMask ) x11display_t x11display; glwstate_t glw_state; static qboolean _xf86_vidmodes_supported = qfalse; static XF86VidModeModeInfo **_xf86_vidmodes; static int _xf86_vidmodes_num; static qboolean _xf86_vidmodes_active = qfalse; static void _xf86_VidmodesInit(void) { int MajorVersion, MinorVersion; // Get video mode list MajorVersion = MinorVersion = 0; if (XF86VidModeQueryVersion (x11display.dpy, &MajorVersion, &MinorVersion)) { Com_Printf("..XFree86-VidMode Extension Version %d.%d\n", MajorVersion, MinorVersion); XF86VidModeGetAllModeLines(x11display.dpy, x11display.scr, &_xf86_vidmodes_num, &_xf86_vidmodes); _xf86_vidmodes_supported = qtrue; } else { Com_Printf("..XFree86-VidMode Extension not available\n"); _xf86_vidmodes_supported = qfalse; } } static void _xf86_VidmodesFree(void) { if (_xf86_vidmodes_supported) XFree(_xf86_vidmodes); _xf86_vidmodes_supported = qfalse; } static void _xf86_VidmodesSwitch(int mode) { if (_xf86_vidmodes_supported) { XF86VidModeSwitchToMode(x11display.dpy, x11display.scr, _xf86_vidmodes[mode]); XF86VidModeSetViewPort(x11display.dpy, x11display.scr, 0, 0); } _xf86_vidmodes_active = qtrue; } static void _xf86_VidmodesSwitchBack(void) { if (_xf86_vidmodes_supported && _xf86_vidmodes_active) _xf86_VidmodesSwitch(0); _xf86_vidmodes_active = 0; } static void _xf86_VidmodesFindBest(int *mode, int *pwidth, int *pheight){ int i, best_fit, best_dist, dist, x, y; best_fit = -1; best_dist = 999999999; if (_xf86_vidmodes_supported) { for (i = 0; i <_xf86_vidmodes_num; i++) { if (_xf86_vidmodes[i]->hdisplay <*pwidth || _xf86_vidmodes[i]->vdisplay <*pheight) continue; x = _xf86_vidmodes[i]->hdisplay - *pwidth; y = _xf86_vidmodes[i]->vdisplay - *pheight; if (x > y) dist = y; else dist = x; if (dist < 0) dist = -dist; // Only positive number please if( dist < best_dist) { best_dist = dist; best_fit = i; } Com_Printf("%ix%i -> %ix%i: %i\n", *pwidth, *pheight, _xf86_vidmodes[i]->hdisplay, _xf86_vidmodes[i]->vdisplay, dist); } if (best_fit >= 0) { Com_Printf("%ix%i selected\n", _xf86_vidmodes[best_fit]->hdisplay, _xf86_vidmodes[best_fit]->vdisplay); *pwidth =_xf86_vidmodes[best_fit]->hdisplay; *pheight =_xf86_vidmodes[best_fit]->vdisplay; } } *mode = best_fit; } static void _x11_SetNoResize(Window w, int width, int height) { XSizeHints *hints; if (x11display.dpy) { hints = XAllocSizeHints(); if (hints) { hints->min_width = hints->max_width = width; hints->min_height = hints->max_height = height; hints->flags = PMaxSize | PMinSize; XSetWMNormalHints(x11display.dpy, w, hints); XFree(hints); } } } /*****************************************************************************/ static void signal_handler(int sig) { printf("Received signal %d, exiting...\n", sig); GLimp_Shutdown(); _exit(0); } static void InitSig(void) { signal(SIGHUP, signal_handler); signal(SIGQUIT, signal_handler); signal(SIGILL, signal_handler); signal(SIGTRAP, signal_handler); signal(SIGIOT, signal_handler); signal(SIGBUS, signal_handler); signal(SIGFPE, signal_handler); signal(SIGSEGV, signal_handler); signal(SIGTERM, signal_handler); } /* ** GLimp_SetMode */ int GLimp_SetMode(int mode, qboolean fullscreen) { int width, height, screen_width, screen_height, screen_mode; float ratio; XSetWindowAttributes wa; unsigned long mask; if (!VID_GetModeInfo( &width, &height, mode)) { Com_Printf( " invalid mode\n" ); return rserr_invalid_mode; } screen_width = width; screen_height = height; if (fullscreen) { _xf86_VidmodesFindBest (&screen_mode, &screen_width, &screen_height); if (screen_mode < 0) { Com_Printf(" no mode found\n"); return rserr_invalid_mode; } if (screen_width < width || screen_height < height) { if (width > height) { ratio = width / height; height = height *ratio; width = screen_width; } else { ratio = height / width; width = width *ratio; height = screen_height; } } Com_Printf ("...setting fullscreen mode %d:", mode); /* Create fulscreen window */ x11display.old_win = x11display.win; wa.background_pixel = 0; wa.border_pixel = 0; wa.event_mask = INIT_MASK; wa.override_redirect = True; mask = CWBackPixel | CWBorderPixel | CWEventMask | CWOverrideRedirect; x11display.win = XCreateWindow(x11display.dpy, x11display.root, 0, 0, screen_width, screen_height, 0, CopyFromParent, InputOutput, CopyFromParent, mask, &wa); XResizeWindow (x11display.dpy, x11display.gl_win, width, height); XReparentWindow (x11display.dpy, x11display.gl_win, x11display.win, (screen_width/2)-(width/2), (screen_height/2)-(height/2)); XMapWindow (x11display.dpy, x11display.gl_win); XMapWindow (x11display.dpy, x11display.win); _x11_SetNoResize (x11display.win, screen_width, screen_height); _xf86_VidmodesSwitch (screen_mode); } else { Com_Printf ("...setting mode %d:", mode); /* Create managed window */ x11display.old_win = x11display.win; wa.background_pixel = 0; wa.border_pixel = 0; wa.event_mask = INIT_MASK; mask = CWBackPixel | CWBorderPixel | CWEventMask; x11display.win = XCreateWindow (x11display.dpy, x11display.root, 0, 0, screen_width, screen_height, 0, CopyFromParent, InputOutput, CopyFromParent, mask, &wa); XSetStandardProperties (x11display.dpy, x11display.win, APPLICATION, None, None, NULL, 0, NULL); x11display.wmDeleteWindow = XInternAtom(x11display.dpy, "WM_DELETE_WINDOW", False); XSetWMProtocols (x11display.dpy, x11display.win, &x11display.wmDeleteWindow, 1); XResizeWindow (x11display.dpy, x11display.gl_win, width, height); XReparentWindow (x11display.dpy, x11display.gl_win, x11display.win, 0, 0); XMapWindow (x11display.dpy, x11display.gl_win); XMapWindow (x11display.dpy, x11display.win); _x11_SetNoResize(x11display.win, width, height); } XSetIconName(x11display.dpy, x11display.win, APPLICATION); XStoreName(x11display.dpy, x11display.win, APPLICATION); // save the parent window size for mouse use. this is not the gl context window x11display.win_width = width; x11display.win_height = height; if (x11display.old_win) { XDestroyWindow (x11display.dpy, x11display.old_win); x11display.old_win = 0; } XFlush (x11display.dpy); glState.width = width; glState.height = height; glState.fullScreen = fullscreen; // let the sound and input subsystems know about the new window VID_NewWindow (width, height); return rserr_ok; } /* ** GLimp_Shutdown */ void GLimp_Shutdown (void) { if (x11display.dpy) { _xf86_VidmodesSwitchBack(); _xf86_VidmodesFree(); if (x11display.cmap) XFreeColormap(x11display.dpy, x11display.cmap); if (x11display.ctx) qglXDestroyContext(x11display.dpy, x11display.ctx); if (x11display.gl_win) XDestroyWindow(x11display.dpy, x11display.gl_win); if (x11display.win) XDestroyWindow(x11display.dpy, x11display.win); XCloseDisplay(x11display.dpy); } x11display.visinfo = NULL; x11display.cmap = 0; x11display.ctx = NULL; x11display.gl_win = 0; x11display.win = 0; x11display.dpy = NULL; } static qboolean gotstencil = qfalse; // evil hack! static qboolean ChooseVisual(int colorbits, int stencilbits) { int colorsize; int depthbits = colorbits; if (colorbits == 24) colorsize = 8; else colorsize = 4; { int attributes[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, colorsize, GLX_GREEN_SIZE, colorsize, GLX_BLUE_SIZE, colorsize, GLX_DEPTH_SIZE, depthbits, GLX_STENCIL_SIZE, stencilbits, None }; x11display.visinfo = qglXChooseVisual(x11display.dpy, x11display.scr, attributes); if (!x11display.visinfo) { Com_Printf("..Failed to get colorbits %i, depthbits %i, stencilbits %i\n", colorbits, depthbits, stencilbits); return qfalse; } else { Com_Printf("..Got colorbits %i, depthbits %i, stencilbits %i\n", colorbits, depthbits, stencilbits); if( stencilbits > 0 ) gotstencil = qtrue; return qtrue; } } } /* ** GLimp_Init */ int GLimp_Init( void *hinstance, void *wndproc) { int colorbits, stencilbits; XSetWindowAttributes attr; unsigned long mask; if (x11display.dpy) GLimp_Shutdown(); InitSig(); Com_Printf("Display initialization\n"); x11display.dpy = XOpenDisplay(NULL); if (!x11display.dpy) { Com_Printf ("..Error couldn't open the X display\n"); return 0; } x11display.scr = DefaultScreen(x11display.dpy); x11display.root = RootWindow(x11display.dpy, x11display.scr); _xf86_VidmodesInit(); if (r_colorbits->integer == 16 || r_colorbits->integer == 24) colorbits = r_colorbits->integer; else colorbits = 0; if (r_stencilbits->integer == 8 || r_stencilbits->integer == 16) stencilbits = r_stencilbits->integer; else stencilbits = 0; gotstencil = qfalse; if (colorbits > 0) { ChooseVisual(colorbits, stencilbits); if (!x11display.visinfo && stencilbits > 8) ChooseVisual(colorbits, 8); if (!x11display.visinfo && stencilbits > 0) ChooseVisual(colorbits, 0); if (!x11display.visinfo && colorbits > 16) ChooseVisual(16, 0); } else { ChooseVisual(24, stencilbits); if (!x11display.visinfo) ChooseVisual(16, stencilbits); if (!x11display.visinfo && stencilbits > 8) ChooseVisual(24, 8); if (!x11display.visinfo && stencilbits > 8) ChooseVisual(16, 8); if (!x11display.visinfo && stencilbits > 0) ChooseVisual(24, 0); if (!x11display.visinfo && stencilbits > 0) ChooseVisual(16, 0); } if (!x11display.visinfo) { GLimp_Shutdown(); // hope this doesn't do anything evil when we don't have window etc. Com_Printf("..Error couldn't set GLX visual\n"); return 0; } glState.stencilEnabled = gotstencil; x11display.ctx = qglXCreateContext(x11display.dpy, x11display.visinfo, NULL, True); x11display.cmap = XCreateColormap(x11display.dpy, x11display.root, x11display.visinfo->visual, AllocNone); attr.colormap = x11display.cmap; attr.border_pixel = 0; attr.event_mask = DISPLAY_MASK; attr.override_redirect = False; mask = CWBorderPixel | CWColormap; x11display.gl_win = XCreateWindow(x11display.dpy, x11display.root, 0, 0, 1, 1, 0, x11display.visinfo->depth, InputOutput, x11display.visinfo->visual, mask, &attr); qglXMakeCurrent(x11display.dpy, x11display.gl_win, x11display.ctx); XSync (x11display.dpy, False); return 1; } /* ** GLimp_BeginFrame */ void GLimp_BeginFrame( void ) { } /* ** GLimp_EndFrame ** ** Responsible for doing a swapbuffers and possibly for other stuff ** as yet to be determined. Probably better not to make this a GLimp ** function and instead do a call to GLimp_SwapBuffers. */ void GLimp_EndFrame (void) { qglXSwapBuffers(x11display.dpy, x11display.gl_win); } /* ** GLimp_BeginFrame */ qboolean GLimp_GetGammaRamp( size_t stride, unsigned short *ramp ) { if (XF86VidModeGetGammaRamp( x11display.dpy, x11display.scr, stride, ramp, ramp + stride, ramp + (stride << 1) ) != 0) return qtrue; return qfalse; } /* ** GLimp_BeginFrame */ void GLimp_SetGammaRamp( size_t stride, unsigned short *ramp ) { XF86VidModeSetGammaRamp(x11display.dpy, x11display.scr, stride, ramp, ramp + stride, ramp + (stride << 1)); } /* ** GLimp_AppActivate */ void GLimp_AppActivate( qboolean active ) { }