blob: f9a03b7fce110b4bd1e8da9438889973b782ca2a [file] [log] [blame]
/******************************************************************************
@File LinuxX11/PVRShellOS.cpp
@Title LinuxX11/PVRShellOS
@Version
@Copyright Copyright (c) Imagination Technologies Limited.
@Platform X11
@Description Makes programming for 3D APIs easier by wrapping window creation
and other functions for use by a demo.
******************************************************************************/
#include <sys/time.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include "PVRShell.h"
#include "PVRShellAPI.h"
#include "PVRShellOS.h"
#include "PVRShellImpl.h"
// No Doxygen for CPP files, due to documentation duplication
/// @cond NO_DOXYGEN
/*!***************************************************************************
Defines
*****************************************************************************/
/*****************************************************************************
Declarations
*****************************************************************************/
static Bool WaitForMapNotify( Display *d, XEvent *e, char *arg );
/*!***************************************************************************
Class: PVRShellInit
*****************************************************************************/
/*!***********************************************************************
@Function PVRShellOutputDebug
@Input format printf style format followed by arguments it requires
@Description Writes the resultant string to the debug output (e.g. using
printf(), OutputDebugString(), ...). Check the SDK release notes for
details on how the string is output.
*************************************************************************/
void PVRShell::PVRShellOutputDebug(char const * const format, ...) const
{
if(!format)
return;
va_list arg;
char buf[1024];
va_start(arg, format);
vsnprintf(buf, 1024, format, arg);
va_end(arg);
// Passes the data to a platform dependant function
m_pShellInit->OsDisplayDebugString(buf);
}
/*!***********************************************************************
@Function OsInit
@description Initialisation for OS-specific code.
*************************************************************************/
void PVRShellInit::OsInit()
{
XInitThreads();
// set values to negative to mark that these are default values
m_pShell->m_pShellData->nShellDimX = -240;
m_pShell->m_pShellData->nShellDimY = -320;
m_X11Display = NULL;
m_X11Visual = NULL;
// Pixmap support: init variables to 0
m_X11Pixmap = BadValue;
/*
Construct the binary path for GetReadPath() and GetWritePath()
*/
// Get PID (Process ID)
pid_t ourPid = getpid();
char *pszExePath, pszSrcLink[64];
int len = 64;
int res;
sprintf(pszSrcLink, "/proc/%d/exe", ourPid);
pszExePath = 0;
do
{
len *= 2;
delete[] pszExePath;
pszExePath = new char[len];
res = readlink(pszSrcLink, pszExePath, len);
if(res < 0)
{
m_pShell->PVRShellOutputDebug("Warning Readlink %s failed. The application name, read path and write path have not been set.\n", pszExePath);
break;
}
} while(res >= len);
if(res >= 0)
{
pszExePath[res] = '\0'; // Null-terminate readlink's result
SetReadPath(pszExePath);
SetWritePath(pszExePath);
SetAppName(pszExePath);
}
delete[] pszExePath;
m_u32ButtonState = 0;
gettimeofday(&m_StartTime,NULL);
}
/*!***********************************************************************
@Function OsInitOS
@description Saves instance handle and creates main window
In this function, we save the instance handle in a global variable and
create and display the main program window.
*************************************************************************/
bool PVRShellInit::OsInitOS()
{
m_X11Display = XOpenDisplay( NULL );
if(!m_X11Display)
{
m_pShell->PVRShellOutputDebug( "Unable to open X display\n");
return false;
}
m_X11Screen = XDefaultScreen( m_X11Display );
/*
If there is a full screen request then
set the window size to the display size.
If there is no full screen request then reduce window size while keeping
the same aspect by dividing the dims by two until it fits inside the display area.
If the position has not changed from its default value, set it to the middle of the screen.
*/
int display_width = XDisplayWidth(m_X11Display,m_X11Screen);
int display_height = XDisplayHeight(m_X11Display,m_X11Screen);
if(m_pShell->m_pShellData->bFullScreen)
{
// For OGL we do real fullscreen for others API set a window of fullscreen size
if(m_pShell->m_pShellData->nShellDimX < 0) {
m_pShell->m_pShellData->nShellDimX = display_width;
}
if(m_pShell->m_pShellData->nShellDimY < 0) {
m_pShell->m_pShellData->nShellDimY = display_height;
}
}
else
{
if(m_pShell->m_pShellData->nShellDimX < 0)
m_pShell->m_pShellData->nShellDimX = (display_width > display_height) ? 800 : 600;
if(m_pShell->m_pShellData->nShellDimY < 0)
m_pShell->m_pShellData->nShellDimY = (display_width > display_height) ? 600 : 800;
if(m_pShell->m_pShellData->nShellDimX > display_width)
m_pShell->m_pShellData->nShellDimX = display_width;
if(m_pShell->m_pShellData->nShellDimY > display_height)
m_pShell->m_pShellData->nShellDimY = display_height;
}
// Create the window
if(!OpenX11Window(*m_pShell))
{
m_pShell->PVRShellOutputDebug( "Unable to open X11 window\n" );
return false;
}
// Pixmap support: create the pixmap
if(m_pShell->m_pShellData->bNeedPixmap)
{
int depth = DefaultDepth(m_X11Display, m_X11Screen);
m_X11Pixmap = XCreatePixmap(m_X11Display,m_X11Window,m_pShell->m_pShellData->nShellDimX,m_pShell->m_pShellData->nShellDimY,depth);
m_X11GC = XCreateGC(m_X11Display,m_X11Window,0,0);
}
return true;
}
/*!***********************************************************************
@Function OsReleaseOS
@description Destroys main window
*************************************************************************/
void PVRShellInit::OsReleaseOS()
{
XCloseDisplay( m_X11Display );
}
/*!***********************************************************************
@Function OsExit
@description Destroys main window
*************************************************************************/
void PVRShellInit::OsExit()
{
// Show the exit message to the user
m_pShell->PVRShellOutputDebug((const char*)m_pShell->PVRShellGet(prefExitMessage));
}
/*!***********************************************************************
@Function OsDoInitAPI
@Return true on success
@description Perform API initialisation and bring up window / fullscreen
*************************************************************************/
bool PVRShellInit::OsDoInitAPI()
{
if(!ApiInitAPI())
{
return false;
}
// No problem occured
return true;
}
/*!***********************************************************************
@Function OsDoReleaseAPI
@description Clean up after we're done
*************************************************************************/
void PVRShellInit::OsDoReleaseAPI()
{
ApiReleaseAPI();
if(m_pShell->m_pShellData->bNeedPixmap)
{
// Pixmap support: free the pixmap
XFreePixmap(m_X11Display,m_X11Pixmap);
XFreeGC(m_X11Display,m_X11GC);
}
CloseX11Window();
}
/*!***********************************************************************
@Function OsRenderComplete
@Returns false when the app should quit
@description Main message loop / render loop
*************************************************************************/
void PVRShellInit::OsRenderComplete()
{
int numMessages;
XEvent event;
char* atoms;
// Are there messages waiting, maybe this should be a while loop
numMessages = XPending( m_X11Display );
for( int i = 0; i < numMessages; i++ )
{
XNextEvent( m_X11Display, &event );
switch( event.type )
{
case ClientMessage:
atoms = XGetAtomName(m_X11Display, event.xclient.message_type);
if (*atoms == *"WM_PROTOCOLS")
{
gShellDone = true;
}
XFree(atoms);
break;
case ButtonRelease:
{
XButtonEvent *button_event = ((XButtonEvent *) &event);
switch( button_event->button )
{
case 1 :
{
m_u32ButtonState &= ~1;
// Set the current pointer location
float vec2PointerLocation[2];
vec2PointerLocation[0] = (float)button_event->x / (float)m_pShell->m_pShellData->nShellDimX;
vec2PointerLocation[1] = (float)button_event->y / (float)m_pShell->m_pShellData->nShellDimY;
TouchEnded(vec2PointerLocation);
}
break;
case 2 : m_u32ButtonState &= ~4; break;
case 3 : m_u32ButtonState &= ~2; break;
default : break;
}
break;
}
case ButtonPress:
{
XButtonEvent *button_event = ((XButtonEvent *) &event);
switch( button_event->button )
{
case 1 :
{
m_u32ButtonState |= 1;
// Set the current pointer location
float vec2PointerLocation[2];
vec2PointerLocation[0] = (float)button_event->x / (float)m_pShell->m_pShellData->nShellDimX;
vec2PointerLocation[1] = (float)button_event->y / (float)m_pShell->m_pShellData->nShellDimY;
TouchBegan(vec2PointerLocation);
}
break;
case 2 : m_u32ButtonState |= 4; break;
case 3 : m_u32ButtonState |= 2; break;
default : break;
}
break;
}
case MotionNotify:
{
XMotionEvent *motion_event = ((XMotionEvent *) &event);
// Set the current pointer location
float vec2PointerLocation[2];
vec2PointerLocation[0] = (float)motion_event->x / (float)m_pShell->m_pShellData->nShellDimX;
vec2PointerLocation[1] = (float)motion_event->y / (float)m_pShell->m_pShellData->nShellDimY;
TouchMoved(vec2PointerLocation);
break;
}
// should SDK handle these?
case MapNotify:
case UnmapNotify:
break;
case KeyPress:
{
XKeyEvent *key_event = ((XKeyEvent *) &event);
switch(key_event->keycode)
{
case 9: nLastKeyPressed = PVRShellKeyNameQUIT; break; // Esc
case 95: nLastKeyPressed = PVRShellKeyNameScreenshot; break; // F11
case 36: nLastKeyPressed = PVRShellKeyNameSELECT; break; // Enter
case 10: nLastKeyPressed = PVRShellKeyNameACTION1; break; // number 1
case 11: nLastKeyPressed = PVRShellKeyNameACTION2; break; // number 2
case 98:
case 111: nLastKeyPressed = m_eKeyMapUP; break;
case 104:
case 116: nLastKeyPressed = m_eKeyMapDOWN; break;
case 100:
case 113: nLastKeyPressed = m_eKeyMapLEFT; break;
case 102:
case 114: nLastKeyPressed = m_eKeyMapRIGHT; break;
default:
break;
}
}
break;
case KeyRelease:
{
// char buf[10];
// XLookupString(&event.xkey,buf,10,NULL,NULL);
// charsPressed[ (int) *buf ] = 0;
}
break;
default:
break;
}
}
}
/*!***********************************************************************
@Function OsPixmapCopy
@Return true if the copy succeeded
@description When using pixmaps, copy the render to the display
*************************************************************************/
bool PVRShellInit::OsPixmapCopy()
{
XCopyArea(m_X11Display,m_X11Pixmap,m_X11Window,m_X11GC,0,0,m_pShell->m_pShellData->nShellDimX,m_pShell->m_pShellData->nShellDimY,0,0);
return true;
}
/*!***********************************************************************
@Function OsGetNativeDisplayType
@Return The 'NativeDisplayType' for EGL
@description Called from InitAPI() to get the NativeDisplayType
*************************************************************************/
void *PVRShellInit::OsGetNativeDisplayType()
{
return m_X11Display;
}
/*!***********************************************************************
@Function OsGetNativePixmapType
@Return The 'NativePixmapType' for EGL
@description Called from InitAPI() to get the NativePixmapType
*************************************************************************/
void *PVRShellInit::OsGetNativePixmapType()
{
// Pixmap support: return the pixmap
return (void*)m_X11Pixmap;
}
/*!***********************************************************************
@Function OsGetNativeWindowType
@Return The 'NativeWindowType' for EGL
@description Called from InitAPI() to get the NativeWindowType
*************************************************************************/
void *PVRShellInit::OsGetNativeWindowType()
{
return (void*)m_X11Window;
}
/*!***********************************************************************
@Function OsGet
@Input prefName Name of value to get
@Modified pn A pointer set to the value asked for
@Returns true on success
@Description Retrieves OS-specific data
*************************************************************************/
bool PVRShellInit::OsGet(const prefNameIntEnum prefName, int *pn)
{
switch( prefName )
{
case prefButtonState:
*pn = m_u32ButtonState;
return true;
default:
return false;
};
return false;
}
/*!***********************************************************************
@Function OsGet
@Input prefName Name of value to get
@Modified pp A pointer set to the value asked for
@Returns true on success
@Description Retrieves OS-specific data
*************************************************************************/
bool PVRShellInit::OsGet(const prefNamePtrEnum prefName, void **pp)
{
return false;
}
/*!***********************************************************************
@Function OsSet
@Input prefName Name of preference to set to value
@Input value Value
@Return true for success
@Description Sets OS-specific data
*************************************************************************/
bool PVRShellInit::OsSet(const prefNameBoolEnum prefName, const bool value)
{
return false;
}
/*!***********************************************************************
@Function OsSet
@Input prefName Name of value to set
@Input i32Value The value to set our named value to
@Returns true on success
@Description Sets OS-specific data
*************************************************************************/
bool PVRShellInit::OsSet(const prefNameIntEnum prefName, const int i32Value)
{
return false;
}
/*!***********************************************************************
@Function OsDisplayDebugString
@Input str string to output
@Description Prints a debug string
*************************************************************************/
void PVRShellInit::OsDisplayDebugString(char const * const str)
{
fprintf(stderr, "%s", str);
}
/*!***********************************************************************
@Function OsGetTime
@Return An incrementing time value measured in milliseconds
@Description Returns an incrementing time value measured in milliseconds
*************************************************************************/
unsigned long PVRShellInit::OsGetTime()
{
timeval tv;
gettimeofday(&tv,NULL);
if(tv.tv_sec < m_StartTime.tv_sec)
m_StartTime.tv_sec = 0;
unsigned long sec = tv.tv_sec - m_StartTime.tv_sec;
return (unsigned long)((sec*(unsigned long)1000) + (tv.tv_usec/1000.0));
}
/*****************************************************************************
Class: PVRShellInitOS
*****************************************************************************/
/*!***********************************************************************
@Function OpenX11Window
@Return true on success
@Description Opens an X11 window. This must be called after
SelectEGLConfiguration() for gEglConfig to be valid
*************************************************************************/
int PVRShellInitOS::OpenX11Window(const PVRShell &shell)
{
XSetWindowAttributes WinAttibutes;
XSizeHints sh;
XEvent event;
unsigned long mask;
#ifdef BUILD_OGL
XF86VidModeModeInfo **modes; // modes of display
int numModes; // number of modes of display
int chosenMode;
int edimx,edimy; //established width and height of the chosen modeline
int i;
#endif
int depth = DefaultDepth(m_X11Display, m_X11Screen);
m_X11Visual = new XVisualInfo;
XMatchVisualInfo( m_X11Display, m_X11Screen, depth, TrueColor, m_X11Visual);
if( !m_X11Visual )
{
shell.PVRShellOutputDebug( "Unable to acquire visual" );
return false;
}
m_X11ColorMap = XCreateColormap( m_X11Display, RootWindow(m_X11Display, m_X11Screen), m_X11Visual->visual, AllocNone );
#ifdef BUILD_OGL
m_i32OriginalModeDotClock = XF86VidModeBadClock;
if(shell.m_pShellData->bFullScreen)
{
// Get mode lines to see if there is requested modeline
XF86VidModeGetAllModeLines(m_X11Display, m_X11Screen, &numModes, &modes);
// look for mode with requested resolution
chosenMode = -1;
i=0;
while((chosenMode == -1)&&(i<numModes))
{
if ((modes[i]->hdisplay == shell.m_pShellData->nShellDimX) && (modes[i]->vdisplay == shell.m_pShellData->nShellDimY))
{
chosenMode = i;
}
++i;
}
// If there is no requested resolution among modelines then terminate
if(chosenMode == -1)
{
shell.PVRShellOutputDebug( "Chosen resolution for full screen mode does not match any modeline available.\n" );
return false;
}
// save desktop-resolution before switching modes
XF86VidModeGetModeLine(m_X11Display,m_X11Screen, &m_i32OriginalModeDotClock, &m_OriginalMode );
XF86VidModeSwitchToMode(m_X11Display, m_X11Screen, modes[chosenMode]);
XF86VidModeSetViewPort(m_X11Display, m_X11Screen, 0, 0);
edimx = modes[chosenMode]->hdisplay;
edimy = modes[chosenMode]->vdisplay;
printf("Fullscreen Resolution %dx%d (chosen mode = %d)\n", edimx, edimy,chosenMode);
XFree(modes);
WinAttibutes.colormap = m_X11ColorMap;
WinAttibutes.background_pixel = 0xFFFFFFFF;
WinAttibutes.border_pixel = 0;
WinAttibutes.override_redirect = true;
// add to these for handling other events
WinAttibutes.event_mask = StructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | Button1MotionMask | KeyPressMask | KeyReleaseMask;
// The diffrence is that we want to ignore influence of window manager for our fullscreen window
mask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap | CWOverrideRedirect;
m_X11Window = XCreateWindow( m_X11Display, RootWindow(m_X11Display, m_X11Screen), 0, 0, edimx, edimy, 0,
CopyFromParent, InputOutput, CopyFromParent, mask, &WinAttibutes);
// keeping the pointer of mouse and keyboard in window to prevent from scrolling the virtual screen
XWarpPointer(m_X11Display, None ,m_X11Window, 0, 0, 0, 0, 0, 0);
// Map and then wait till mapped, grabbing should be after mapping the window
XMapWindow( m_X11Display, m_X11Window );
XGrabKeyboard(m_X11Display, m_X11Window, True, GrabModeAsync, GrabModeAsync, CurrentTime);
XGrabPointer(m_X11Display, m_X11Window, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, m_X11Window, None, CurrentTime);
XIfEvent( m_X11Display, &event, WaitForMapNotify, (char*)m_X11Window );
}
else
#endif
{
// For OGLES we assume that chaning of video mode is not available (freedesktop does not allow to do it)
// so if requested resolution differs from the display dims then we quit
#ifndef BUILD_OGL
int display_width = XDisplayWidth(m_X11Display,m_X11Screen);
int display_height = XDisplayHeight(m_X11Display,m_X11Screen);
if((shell.m_pShellData->bFullScreen)&&((shell.m_pShellData->nShellDimX != display_width)||(shell.m_pShellData->nShellDimY != display_height)) ) {
shell.PVRShellOutputDebug( "Chosen resolution for full screen mode does not match available modeline.\n" );
return false;
}
#endif
WinAttibutes.colormap = m_X11ColorMap;
WinAttibutes.background_pixel = 0xFFFFFFFF;
WinAttibutes.border_pixel = 0;
// add to these for handling other events
WinAttibutes.event_mask = StructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | Button1MotionMask | KeyPressMask | KeyReleaseMask;
// The attribute mask
mask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap ;
m_X11Window = XCreateWindow( m_X11Display, // Display
RootWindow(m_X11Display, m_X11Screen), // Parent
shell.m_pShellData->nShellPosX, // X position of window
shell.m_pShellData->nShellPosY, // Y position of window
shell.m_pShellData->nShellDimX, // Window width
shell.m_pShellData->nShellDimY, // Window height
0, // Border width
CopyFromParent, // Depth (taken from parent)
InputOutput, // Window class
CopyFromParent, // Visual type (taken from parent)
mask, // Attributes mask
&WinAttibutes); // Attributes
// Set the window position
sh.flags = USPosition;
sh.x = shell.m_pShellData->nShellPosX;
sh.y = shell.m_pShellData->nShellPosY;
XSetStandardProperties( m_X11Display, m_X11Window, shell.m_pShellData->pszAppName, shell.m_pShellData->pszAppName, None, 0, 0, &sh );
// Map and then wait till mapped
XMapWindow( m_X11Display, m_X11Window );
XIfEvent( m_X11Display, &event, WaitForMapNotify, (char*)m_X11Window );
// An attempt to hide a border for fullscreen on non OGL apis (OGLES,OGLES2)
if(shell.m_pShellData->bFullScreen)
{
XEvent xev;
Atom wmState = XInternAtom(m_X11Display, "_NET_WM_STATE", False);
Atom wmStateFullscreen = XInternAtom(m_X11Display, "_NET_WM_STATE_FULLSCREEN", False);
memset(&xev, 0, sizeof(XEvent));
xev.type = ClientMessage;
xev.xclient.window = m_X11Window;
xev.xclient.message_type = wmState;
xev.xclient.format = 32;
xev.xclient.data.l[0] = 1;
xev.xclient.data.l[1] = wmStateFullscreen;
xev.xclient.data.l[2] = 0;
XSendEvent(m_X11Display, RootWindow(m_X11Display, m_X11Screen), False, SubstructureNotifyMask, &xev);
}
Atom wmDelete = XInternAtom(m_X11Display, "WM_DELETE_WINDOW", True);
XSetWMProtocols(m_X11Display, m_X11Window, &wmDelete, 1);
XSetWMColormapWindows( m_X11Display, m_X11Window, &m_X11Window, 1 );
}
XFlush( m_X11Display );
return true;
}
/*!***********************************************************************
@Function CloseX11Window
@Return void
@Description destroy the instance of a window, and release all relevent memory
*************************************************************************/
void PVRShellInitOS::CloseX11Window()
{
// revert introductional resolution (full screen case, bad clock is default value meaning that good was not acquired)
#ifdef BUILD_OGL
XF86VidModeModeInfo tmpmi;
if(m_i32OriginalModeDotClock != XF86VidModeBadClock)
{
// revert desktop-resolution (stored previously) before exiting
tmpmi.dotclock = m_i32OriginalModeDotClock;
tmpmi.c_private = m_OriginalMode.c_private;
tmpmi.flags = m_OriginalMode.flags;
tmpmi.hdisplay = m_OriginalMode.hdisplay;
tmpmi.hskew = m_OriginalMode.hskew;
tmpmi.hsyncend = m_OriginalMode.hsyncend;
tmpmi.hsyncstart = m_OriginalMode.hsyncstart;
tmpmi.htotal = m_OriginalMode.htotal;
tmpmi.privsize = m_OriginalMode.privsize;
tmpmi.vdisplay = m_OriginalMode.vdisplay;
tmpmi.vsyncend = m_OriginalMode.vsyncend;
tmpmi.vsyncstart = m_OriginalMode.vsyncstart;
tmpmi.vtotal = m_OriginalMode.vtotal;
XF86VidModeSwitchToMode(m_X11Display,m_X11Screen,&tmpmi);
}
#endif
XDestroyWindow( m_X11Display, m_X11Window );
XFreeColormap( m_X11Display, m_X11ColorMap );
if(m_X11Visual)
delete m_X11Visual;
}
/*****************************************************************************
Global code
*****************************************************************************/
static Bool WaitForMapNotify( Display *d, XEvent *e, char *arg )
{
return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
}
/*!***************************************************************************
@function main
@input argc count of args from OS
@input argv array of args from OS
@returns result code to OS
@description Main function of the program
*****************************************************************************/
int main(int argc, char **argv)
{
PVRShellInit init;
// Initialise the demo, process the command line, create the OS initialiser.
if(!init.Init())
return EXIT_ERR_CODE;
init.CommandLine((argc-1),&argv[1]);
// Initialise/run/shutdown
while(init.Run());
return EXIT_NOERR_CODE;
}
/// @endcond
/*****************************************************************************
End of file (PVRShellOS.cpp)
*****************************************************************************/