ManiaDrive/raydium/myglut-x11.c
2025-01-26 18:33:45 +01:00

661 lines
17 KiB
C

/*
Raydium - CQFD Corp.
http://raydium.org/
License: GPL - GNU General Public License, see "gpl.txt" file.
*/
#include "headers/myglut.h"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
//#include <X11/Xutil.h> // provided by glx
#include <GL/glx.h>
#ifdef HAVE_XINERAMA
#include <X11/extensions/Xinerama.h>
#endif
#ifdef HAVE_MOTIF
#include <X11/Xm/MwmUtil.h>
#else
/* bit definitions for MwmHints.flags */
#define MWM_HINTS_FUNCTIONS (1L << 0)
#define MWM_HINTS_DECORATIONS (1L << 1)
#define MWM_HINTS_INPUT_MODE (1L << 2)
#define MWM_HINTS_STATUS (1L << 3)
/* bit definitions for MwmHints.decorations */
#define MWM_DECOR_ALL (1L << 0)
#define MWM_DECOR_BORDER (1L << 1)
#define MWM_DECOR_RESIZEH (1L << 2)
#define MWM_DECOR_TITLE (1L << 3)
#define MWM_DECOR_MENU (1L << 4)
#define MWM_DECOR_MINIMIZE (1L << 5)
#define MWM_DECOR_MAXIMIZE (1L << 6)
typedef struct
{
unsigned long flags ;
unsigned long functions ;
unsigned long decorations ;
long inputMode ;
unsigned long status ;
} PropMotifWmHints ;
#define PROP_MOTIF_WM_HINTS_ELEMENTS 5
#endif
#ifndef GLX_SAMPLE_BUFFERS_ARB
#define GLX_SAMPLE_BUFFERS_ARB 100000
#define GLX_SAMPLES_ARB 100001
#endif
Display *currDisplay ;
XVisualInfo *visualInfo ;
Window currHandle ;
GLXContext currContext ;
Window rootWindow ;
Atom delWinAtom ;
int currScreen;
int currConnect;
typedef struct PixelFormat
{
int num_samples ;
int bits_per_pixel ;
int z_bits ;
int stencil_bits ;
} PixelFormat;
signed char XineramaAndFullscreenFocusHack=0;
signed char FullscreenFlag=0;
PixelFormat preferred_pixel_formats [] =
{
/* NumSamples, RGB_bits, Z_bits, Stencil */
{ 0, 24, 24, 1 }, /* Progressively nastier image formats */
{ 0, 16, 24, 1 },
{ 0, 16, 16, 1 },
{ 0, 16, 16, 0 },
{ -1, -1, -1, -1 } /* Magic end marker */
} ;
void pwInit ( int x, int y, int w, int h, int multisample,
char *title, int border, int num_samples );
int raydium_init_cli_option(char *option, char *value);
// ---------------------- public API
void glutInit(int *argc, char **argv)
{
currDisplay = NULL ;
visualInfo = NULL ;
currScreen = 0;
currConnect = 0;
glutReshapeFuncCB=NULL;
glutKeyboardFuncCB=NULL;
glutSpecialUpFuncCB=NULL;
glutSpecialFuncCB=NULL;
glutMotionFuncCB=NULL;
glutPassiveMotionFuncCB=NULL;
glutMouseFuncCB=NULL;
glutDisplayFuncCB=NULL;
glutIdleFuncCB=NULL;
_glutMouseVisible=1;
}
void mylgutCloseWindow(void)
{
glXDestroyContext ( currDisplay, currContext ) ;
XDestroyWindow ( currDisplay, currHandle ) ;
XFlush ( currDisplay ) ;
}
//glutSetCursor
void glutSetCursor(int cursor)
{
int currCursor;
Pixmap pix ;
char blank_cursor[16*16];
XColor bcol = { 0 } ;
switch(cursor)
{
case GLUT_CURSOR_LEFT_ARROW:
currCursor = XC_left_ptr;
XDefineCursor( currDisplay, currHandle,
XCreateFontCursor ( currDisplay, currCursor ) ) ;
_glutMouseVisible=1;
break;
case GLUT_CURSOR_NONE:
default:
memset(blank_cursor,0,16*16);
pix = XCreateBitmapFromData ( currDisplay,
rootWindow,
blank_cursor, 16, 16 ) ;
XDefineCursor ( currDisplay, currHandle,
XCreatePixmapCursor ( currDisplay,
pix, pix, &bcol, &bcol, 0, 0 ) ) ;
XFreePixmap ( currDisplay, pix ) ;
_glutMouseVisible=1;
}
}
//glutWarpPointer (move mouse)
void glutWarpPointer(int x, int y)
{
XWarpPointer(currDisplay, None, currHandle, 0, 0, 0, 0, x, y);
XFlush(currDisplay);
}
//glutSwapBuffers
void glutSwapBuffers(void)
{
glFlush () ;
glXSwapBuffers ( currDisplay, currHandle ) ;
// get events ?
}
//glutMainLoop is generic (myglut.c)
// ------------- private part
void chooseVisual (PixelFormat *pf)
{
int attribs [ 100 ] ;
int n = 0 ;
attribs [n++] = GLX_RGBA ;
switch ( pf->bits_per_pixel )
{
case 3 :
attribs [n++] = GLX_RED_SIZE ; attribs [n++] = 1 ;
attribs [n++] = GLX_GREEN_SIZE ; attribs [n++] = 1 ;
attribs [n++] = GLX_BLUE_SIZE ; attribs [n++] = 1 ;
break ;
case 16 :
attribs [n++] = GLX_RED_SIZE ; attribs [n++] = 5 ;
attribs [n++] = GLX_GREEN_SIZE ; attribs [n++] = 6 ;
attribs [n++] = GLX_BLUE_SIZE ; attribs [n++] = 5 ;
break ;
case 24 :
attribs [n++] = GLX_RED_SIZE ; attribs [n++] = 8 ;
attribs [n++] = GLX_GREEN_SIZE ; attribs [n++] = 8 ;
attribs [n++] = GLX_BLUE_SIZE ; attribs [n++] = 8 ;
break ;
}
switch ( pf->z_bits )
{
case 1 : attribs [n++] = GLX_DEPTH_SIZE ; attribs [n++] = 1 ; break ;
case 16 : attribs [n++] = GLX_DEPTH_SIZE ; attribs [n++] = 16 ; break ;
case 24 : attribs [n++] = GLX_DEPTH_SIZE ; attribs [n++] = 24 ; break ;
case 32 : attribs [n++] = GLX_DEPTH_SIZE ; attribs [n++] = 32 ; break ;
}
switch ( pf->stencil_bits )
{
case 1 : attribs [n++] = GLX_STENCIL_SIZE ; attribs [n++] = 1 ; break ;
case 8 : attribs [n++] = GLX_STENCIL_SIZE ; attribs [n++] = 8 ; break ;
}
if ( pf->num_samples > 0 )
{
attribs [n++] = GLX_SAMPLE_BUFFERS_ARB ; attribs [n++] = 1 ;
attribs [n++] = GLX_SAMPLES_ARB ; attribs [n++] = pf->num_samples ;
}
attribs [n++] = GLX_DOUBLEBUFFER ;
attribs [n++] = None ;
visualInfo = glXChooseVisual ( currDisplay, currScreen, attribs ) ;
}
void pwInit ( int x, int y, int w, int h, int multisample,
char *title, int border, int num_samples )
{
char *displayName = NULL;
int i;
int origin[2];
int size[2];
int DispX,DispY; // X screen size
XSetWindowAttributes attribs ;
XTextProperty textProperty ;
XSizeHints sizeHints ;
XWMHints wmHints ;
unsigned int mask ;
PixelFormat pf ;
#ifdef HAVE_XINERAMA
int i_d1, i_d2;
#endif
PropMotifWmHints hints ;
Atom prop_t ;
Atom prop ;
displayName=getenv ( "DISPLAY" ) ;
if ( displayName == NULL ) displayName = ":0.0" ;
currDisplay = XOpenDisplay ( displayName ) ;
if ( currDisplay == NULL )
{
raydium_log("(my)glut: ERROR: Can't open display '%s'",
XDisplayName ( displayName ) ) ;
exit ( 1 ) ;
}
/* OpenGL GLX extension availability? */
if ( ! glXQueryExtension ( currDisplay, NULL, NULL ) )
{
raydium_log("(my)glut: ERROR: GLX extension not available on display '%s'",
XDisplayName ( displayName ) ) ;
exit ( 1 ) ;
}
currScreen = DefaultScreen ( currDisplay ) ;
rootWindow = RootWindow ( currDisplay, currScreen ) ;
currConnect = ConnectionNumber ( currDisplay ) ;
delWinAtom = XInternAtom ( currDisplay, "WM_DELETE_WINDOW", 0 ) ;
DispX = DisplayWidth ( currDisplay, currScreen ) ;
DispY = DisplayHeight ( currDisplay, currScreen ) ;
#ifdef HAVE_XINERAMA
if(XineramaQueryExtension( currDisplay, &i_d1, &i_d2 )
&& XineramaIsActive( currDisplay ) )
{
XineramaScreenInfo *screens;
int num_screens;
int selected;
char str[RAYDIUM_MAX_NAME_LEN];
screens = XineramaQueryScreens(currDisplay,&num_screens);
raydium_log("Xinerama detected with %i screens:",num_screens);
for(i=0;i<num_screens;i++)
{
raydium_log("*** screen %i : %ix%i at (%i,%i)",i,screens[i].width,screens[i].height,screens[i].x_org,screens[i].y_org);
}
if(raydium_init_cli_option("xinerama-fullscreen",NULL))
{
raydium_log("... but using Xinerama fullscreen anyway !");
}
else
{
if(raydium_init_cli_option("xinerama-screen",str))
selected=atoi(str);
else
selected=0;
if(selected<0 || selected >=num_screens)
{
raydium_log("invalid screen id !");
selected=0;
}
raydium_log("using Xinerama screen %i",selected);
x+=screens[selected].x_org;
y+=screens[selected].y_org;
DispX=screens[selected].width;
DispY=screens[selected].height;
if(w==-1 && h==-1) XineramaAndFullscreenFocusHack=1;
}
XFree(screens);
} // end Xinerama
else
{
raydium_log("no Xinerama on this display");
}
#else
raydium_log("no Xinerama support. See config.h for HAVE_XINERAMA symbol");
#endif
if ( w == -1 && h == -1)
{
w = DispX;
h = DispY;
FullscreenFlag = 1;
}
origin [ 0 ] = x ;
origin [ 1 ] = y ;
size [ 0 ] = w ;
size [ 1 ] = h ;
for (i = 0 ; preferred_pixel_formats [ i ] . num_samples >= 0 ; i++ )
{
pf = preferred_pixel_formats [ i ] ;
pf . num_samples = num_samples ;
chooseVisual ( &pf ) ;
if ( visualInfo != NULL )
break ;
}
if ( visualInfo == NULL )
{
num_samples = 0 ;
for (i = 0 ; preferred_pixel_formats [ i ] . num_samples >= 0 ; i++ )
{
pf = preferred_pixel_formats [ i ] ;
pf . num_samples = num_samples ;
chooseVisual ( &pf ) ;
if ( visualInfo != NULL )
break ;
}
if ( visualInfo == NULL )
{
raydium_log("(my)glut: ERROR: Unable to open a suitable window");
exit ( 1 ) ;
}
}
attribs.event_mask = StructureNotifyMask | ExposureMask |
ButtonPressMask | ButtonReleaseMask |
KeyPressMask | KeyReleaseMask |
EnterWindowMask | LeaveWindowMask |
PointerMotionMask | ButtonMotionMask |
VisibilityChangeMask ;
attribs.background_pixmap = None ;
attribs.background_pixel = 0 ;
attribs.border_pixel = 0 ;
attribs.colormap = XCreateColormap ( currDisplay, rootWindow,
visualInfo->visual, AllocNone ) ;
mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
if(FullscreenFlag)
{
attribs.override_redirect = True;
mask |= CWOverrideRedirect;
}
currHandle = XCreateWindow ( currDisplay, rootWindow,
x, y, w, h, 0, visualInfo->depth,
InputOutput, visualInfo->visual,
mask, &attribs ) ;
currContext = glXCreateContext ( currDisplay, visualInfo, NULL, 1 ) ;
glXMakeCurrent ( currDisplay, currHandle, currContext ) ;
if ( ! glXIsDirect ( currDisplay, glXGetCurrentContext() ) )
{
raydium_log("(my)glut: WARNING: This is an *INDIRECT* rendering context.") ;
}
sizeHints.flags = 0 ;
if ( x >= 0 && y >= 0 )
sizeHints.flags |= USPosition ;
sizeHints.flags |= USSize ;
sizeHints.x = x ; sizeHints.y = y ;
sizeHints.width = w ; sizeHints.height = h ;
if(FullscreenFlag)
{
// make this window unresizable
sizeHints.flags |= PMinSize;
sizeHints.flags |= PMaxSize;
sizeHints.min_width=w;
sizeHints.max_width=w;
sizeHints.min_height=h;
sizeHints.max_height=h;
}
wmHints.flags = StateHint;
wmHints.initial_state = NormalState ;
hints . flags = MWM_HINTS_DECORATIONS ;
hints . decorations = border ? MWM_DECOR_ALL : 0 ;
prop_t = prop = XInternAtom ( currDisplay, "_MOTIF_WM_HINTS", True ) ;
if ( prop != 0 )
XChangeProperty ( currDisplay, currHandle, prop, prop_t, 32,
PropModeReplace, (unsigned char *) &hints,
PROP_MOTIF_WM_HINTS_ELEMENTS) ;
XStringListToTextProperty ( (char **) &title, 1, &textProperty ) ;
XSetWMProperties ( currDisplay, currHandle,
&textProperty, &textProperty, 0, 0,
&sizeHints, &wmHints, NULL ) ;
XSetWMProtocols ( currDisplay, currHandle, &delWinAtom , 1 );
XMapWindow ( currDisplay, currHandle ) ;
glXMakeCurrent ( currDisplay, currHandle, currContext ) ;
glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
glClear ( GL_COLOR_BUFFER_BIT ) ;
glutSwapBuffers();
glClear ( GL_COLOR_BUFFER_BIT ) ;
glutSwapBuffers();
raydium_log("Found %ix%i with %i bpp color and %i bits zbuffer (stencil is %i)",sizeHints.width,sizeHints.height,pf.bits_per_pixel,pf.z_bits,pf.stencil_bits);
_glutWindowSize[0]=sizeHints.width;
_glutWindowSize[1]=sizeHints.height;
if(FullscreenFlag)
XGrabKeyboard(currDisplay,currHandle,False,GrabModeAsync,GrabModeAsync,CurrentTime);
// XSetInputFocus(currDisplay,currHandle,RevertToNone,CurrentTime);
}
void myglutGetEvents (void)
{
static int size[2]={-1,-1};
XEvent event ;
XComposeStatus composeStatus ;
char asciiCode [ 32 ] ;
KeySym keySym ;
int len,result;
signed char special=0;
// insideCallback = true ;
while ( XPending ( currDisplay ) )
{
int updown = GLUT_DOWN ;
XNextEvent ( currDisplay, &event ) ;
// refreshModifiers ( &event ) ;
switch ( event.type )
{
case ClientMessage : exit(0) ; break ;
case DestroyNotify : exit(0) ; break ;
case ConfigureNotify :
if ( currHandle == event.xconfigure.window &&
( size[0] != event.xconfigure.width ||
size[1] != event.xconfigure.height ) )
{
size[0] = event.xconfigure.width ;
size[1] = event.xconfigure.height ;
glXMakeCurrent ( currDisplay, currHandle, currContext ) ;
glXWaitX () ;
if (glutReshapeFuncCB)
glutReshapeFuncCB(size[0], size[1]);
}
break;
case MappingNotify:
XRefreshKeyboardMapping ( (XMappingEvent *) &event ) ;
break;
case EnterNotify :
if(XineramaAndFullscreenFocusHack)
{
XSetInputFocus(currDisplay,currHandle,RevertToParent,CurrentTime);
XRaiseWindow(currDisplay,currHandle);
}
break;
case LeaveNotify :
case VisibilityNotify:
case Expose : break ;
case MotionNotify :
if (glutPassiveMotionFuncCB)
glutPassiveMotionFuncCB( event.xmotion.x, event.xmotion.y);
break ;
case ButtonRelease :
updown = GLUT_UP ;
// FALLTHROUGH
case ButtonPress :
{
if (glutMouseFuncCB)
glutMouseFuncCB(event.xbutton.button-1, updown, event.xbutton.x, event.xbutton.y ) ;
}
break ;
case KeyRelease :
updown = GLUT_UP ;
// FALLTHROUGH
case KeyPress :
len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
&keySym, &composeStatus ) ;
result = -1 ;
if( len > 0 )
result = asciiCode[ 0 ] ;
else
{
special=1;
switch( keySym )
{
case XK_F1: result = GLUT_KEY_F1; break;
case XK_F2: result = GLUT_KEY_F2; break;
case XK_F3: result = GLUT_KEY_F3; break;
case XK_F4: result = GLUT_KEY_F4; break;
case XK_F5: result = GLUT_KEY_F5; break;
case XK_F6: result = GLUT_KEY_F6; break;
case XK_F7: result = GLUT_KEY_F7; break;
case XK_F8: result = GLUT_KEY_F8; break;
case XK_F9: result = GLUT_KEY_F9; break;
case XK_F10: result = GLUT_KEY_F10; break;
case XK_F11: result = GLUT_KEY_F11; break;
case XK_F12: result = GLUT_KEY_F12; break;
case XK_KP_Left:
case XK_Left: result = GLUT_KEY_LEFT; break;
case XK_KP_Right:
case XK_Right: result = GLUT_KEY_RIGHT; break;
case XK_KP_Up:
case XK_Up: result = GLUT_KEY_UP; break;
case XK_KP_Down:
case XK_Down: result = GLUT_KEY_DOWN; break;
case XK_KP_Prior:
case XK_Prior: result = GLUT_KEY_PAGE_UP; break;
case XK_KP_Next:
case XK_Next: result = GLUT_KEY_PAGE_DOWN; break;
case XK_KP_Home:
case XK_Home: result = GLUT_KEY_HOME; break;
case XK_KP_End:
case XK_End: result = GLUT_KEY_END; break;
case XK_KP_Insert:
case XK_Insert: result = GLUT_KEY_INSERT; break;
}
}
if(result!=-1)
{
// check autorepeat with current KeyRelease and next event
if (special && XEventsQueued(currDisplay, QueuedAfterReading))
{
XEvent ahead;
XPeekEvent(currDisplay, &ahead);
if (ahead.type == KeyPress && event.type == KeyRelease
&& ahead.xkey.window == event.xkey.window
&& ahead.xkey.keycode == event.xkey.keycode
&& ahead.xkey.time == event.xkey.time)
{
// repeating key. Discard event.
break;
}
}
// special down
if(special && updown==GLUT_DOWN && glutSpecialFuncCB && !raydium_key[result])
glutSpecialFuncCB(result,event.xkey.x, event.xkey.y);
// special up
if(special && updown==GLUT_UP && glutSpecialUpFuncCB && raydium_key[result])
glutSpecialUpFuncCB(result,event.xkey.x, event.xkey.y);
// normal
if(!special && updown==GLUT_DOWN && glutKeyboardFuncCB)
glutKeyboardFuncCB(result,event.xkey.x, event.xkey.y);
}
break ;
}
}
// insideCallback = false ;
glXMakeCurrent ( currDisplay, currHandle, currContext ) ;
}