Descent3/lnxmvelib/lnxdraw.cpp

767 lines
25 KiB
C++

/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include "linux/lnxdraw.h"
#include "lnxscreenmode.h"
// static Display *lpDisplay = NULL;
static int nDefaultScreen = -1;
static unsigned int dwOriginalWidth, dwOriginalHeight;
static LnxWindow **WindowList;
static int NumWindows = 0;
// static int GetXSharedMemory(int size);
inline void BltBuffer16ToPixMap24(unsigned char *pixmap, unsigned char *buffer, int width, int height);
inline void BltBuffer32ToPixMap24(unsigned char *pixmap, unsigned char *buffer, int width, int height);
inline void BltBuffer16ToPixMap16(unsigned char *pixmap, unsigned char *buffer, int width, int height);
inline void BltBuffer32ToPixMap16(unsigned char *pixmap, unsigned char *buffer, int width, int height);
static SDL_Rect dispSize;
//////////////////////
// LnxDraw_InitVideo
//////////////////////
// Initializes the Linux video system (for X-Windows)
//
// Returns:
// 0 : no error
// -1 : invalid parameter
// -2 : already initialized
int LnxDraw_InitVideo(LnxVideoDesc *ldesc) {
return -1;
if (!ldesc)
return -1;
// if(lpDisplay)
// return -2;
// lpDisplay = ldesc->dDisplay;
// nDefaultScreen = ldesc->nScreen;
// dwOriginalWidth = DisplayWidth(lpDisplay,nDefaultScreen);
// dwOriginalHeight = DisplayHeight(lpDisplay,nDefaultScreen);
memset(&dispSize, '\0', sizeof(dispSize));
return 0;
}
int d3SDLEventFilter(const SDL_Event *event);
/////////////////////////
// LnxDraw_CreateWindow
/////////////////////////
// Creates and displays a window
//
// Returns:
// 0 : no error (handle in lphandle)
// -1 : invalid parameter
// -2 : Display not opened
// -3 : Out of memory
int LnxDraw_CreateWindow(LnxWindowDesc *ldesc, LnxWindow **lphandle) {
return -1;
if (!ldesc || !lphandle)
return -1;
*lphandle = NULL;
// rcg09182000 don't need to quitsubsystem anymore...
// SDL_QuitSubSystem(SDL_INIT_VIDEO); // here goes nothing...
SDL_ClearError();
int rc = SDL_Init(SDL_INIT_VIDEO);
if (rc != 0) {
fprintf(stderr, "SDL: SDL_Init() failed! rc == (%d).\n", rc);
fprintf(stderr, "SDL_GetError() reports \"%s\".\n", SDL_GetError());
return (-2);
} // if
SDL_SetEventFilter(d3SDLEventFilter);
SDL_Rect **modes = LinuxVideoMode.getModes();
Uint32 sdlflags = LinuxVideoMode.getSDLFlags();
// if(!lpDisplay)
// return -2;
// determine what we have to work with (currently)
// unsigned int display_width,display_height;
// display_width = DisplayWidth(lpDisplay,nDefaultScreen);
// display_height = DisplayHeight(lpDisplay,nDefaultScreen);
// allocate a window for use
LnxWindow *wnd;
wnd = (LnxWindow *)malloc(sizeof(*wnd));
if (!wnd) {
return -3;
}
int i = 0;
dispSize.x = dispSize.y = 0;
dispSize.w = ldesc->dwWidth;
dispSize.h = ldesc->dwHeight;
// need these two lines for a Voodoo3 bug.
SDL_ShowCursor(0);
SDL_WM_GrabInput(SDL_GRAB_ON);
wnd->surface = SDL_SetVideoMode(ldesc->dwWidth, ldesc->dwHeight, ldesc->bpp, sdlflags);
// failed? Try a window.
if ((wnd->surface == NULL) && (sdlflags & SDL_FULLSCREEN)) {
sdlflags &= ~SDL_FULLSCREEN;
wnd->surface = SDL_SetVideoMode(ldesc->dwWidth, ldesc->dwHeight, ldesc->bpp, sdlflags);
} // if
SDL_WM_GrabInput(SDL_GRAB_OFF);
if (!(sdlflags & SDL_FULLSCREEN))
SDL_ShowCursor(1);
if (wnd->surface == NULL) {
fprintf(stderr, "ERROR: SDL could not set the video mode!\n");
return -3;
} // if
wnd->fullScreen = (sdlflags & SDL_FULLSCREEN) ? true : false;
SDL_WM_SetCaption("Descent 3", NULL);
/*
wnd->WindowPreCreated = (ldesc->dwFlags&LNXDRAWF_USEPRECREATEDWIN)?true:false;
if(!wnd->WindowPreCreated)
{
// allocate what the hints, etc.
wnd->lpSizeHints = XAllocSizeHints();
if(!wnd->lpSizeHints)
{
// TODO: Free above allocated hints
free(wnd);
return -3;
}
wnd->lpWmHints = XAllocWMHints();
if(!wnd->lpWmHints)
{
// TODO: Free above allocated hints
free(wnd);
return -3;
}
wnd->lpClassHints = XAllocClassHint();
if(!wnd->lpClassHints)
{
// TODO: Free above allocated hints
free(wnd);
return -3;
}
}
// try to match a visual
if (!XMatchVisualInfo(lpDisplay,nDefaultScreen,16,TrueColor,&wnd->viVisualInfo))
{
fprintf(stderr,"Error: Unable to get 16bit TrueColor Visual\n");
// TODO: Free above allocated hints
free(wnd);
return -3;
}
// see if we have shared memory available
wnd->bHaveSharedMemory = (bool)(XShmQueryExtension(lpDisplay)!=0);
wnd->shmInfo.shmaddr = NULL;
// even if the display says it has shared memory, it still might not be
// available (if we are not running on a local connection)
if(wnd->bHaveSharedMemory)
{
char *ptr;
char *displayname = (char *)getenv("DISPLAY");
if (displayname)
{
ptr = displayname;
while (*ptr && (*ptr != ':')) ptr++;
if (*ptr) *ptr = '\0';
if (!(!strcasecmp(displayname, "unix") || !*displayname))
wnd->bHaveSharedMemory = false;
}else
wnd->bHaveSharedMemory = false;
}
//fprintf(stdout,"Shared Memory: %savailable\n",wnd->bHaveSharedMemory?"":"Not ");
if(!wnd->WindowPreCreated)
{
wnd->lpXvisual = wnd->viVisualInfo.visual;
wnd->cmColorMap = DefaultColormapOfScreen(DefaultScreenOfDisplay(lpDisplay));
unsigned long attrib_mask;
XSetWindowAttributes attrib;
// setup some attribute and hints for actual window creation
attrib_mask = CWEventMask | CWColormap | CWBorderPixel;
attrib.event_mask = KeyPressMask | KeyReleaseMask | PointerMotionMask |
ButtonPressMask | ButtonReleaseMask | ExposureMask;
attrib.colormap = wnd->cmColorMap;
attrib.border_pixel = 0;
wnd->lpSizeHints->width = ldesc->dwWidth;
wnd->lpSizeHints->height = ldesc->dwHeight;
wnd->lpSizeHints->min_width = ldesc->dwWidth;
wnd->lpSizeHints->max_width = ldesc->dwWidth;
wnd->lpSizeHints->min_height = ldesc->dwHeight;
wnd->lpSizeHints->max_height = ldesc->dwHeight;
wnd->lpSizeHints->x = ldesc->dwXPos;
wnd->lpSizeHints->y = ldesc->dwYPos;
wnd->lpSizeHints->flags |= USSize | PMinSize | PMaxSize | USPosition;
wnd->wWindow = XCreateWindow(lpDisplay,RootWindow(lpDisplay,nDefaultScreen),
ldesc->dwXPos,ldesc->dwYPos,
ldesc->dwWidth,ldesc->dwHeight,
0,
wnd->viVisualInfo.depth,
InputOutput,
wnd->lpXvisual,
attrib_mask,
&attrib);
XStoreName(lpDisplay,wnd->wWindow,ldesc->lpszName);
XSetIconName(lpDisplay,wnd->wWindow,ldesc->lpszName);
XSetWMNormalHints(lpDisplay,wnd->wWindow,wnd->lpSizeHints);
// Display the window!
XMapWindow(lpDisplay,wnd->wWindow);
// Wait until it is actually visible
bool wait_for_draw = false;
XEvent event;
while (!wait_for_draw)
{
XNextEvent(lpDisplay, &event);
if (event.type == Expose && !event.xexpose.count) wait_for_draw = true;
}
}else
{
wnd->wWindow = *ldesc->pre_created_window;
// resize window and stuff here
wnd->viVisualInfo = ldesc->pre_created_visinfo;
wnd->lpXvisual = wnd->viVisualInfo.visual;
wnd->cmColorMap = DefaultColormapOfScreen(DefaultScreenOfDisplay(lpDisplay));
}
*/
// We're done, add it to our Window list
WindowList = (LnxWindow **)realloc(WindowList, sizeof(LnxWindow *) * (NumWindows + 1));
if (!WindowList) {
return -3;
}
WindowList[NumWindows] = wnd;
*lphandle = wnd;
NumWindows++;
// Setup window for blitting
wnd->bLocked = false;
wnd->dwWidth = ldesc->dwWidth;
wnd->dwHeight = ldesc->dwHeight;
// Create the Graphics Context
// int valuemask;
// XGCValues xgcvalues;
// xgcvalues.graphics_exposures = False;
// valuemask = GCGraphicsExposures;
// wnd->m_GC = XCreateGC(lpDisplay,wnd->wWindow,valuemask,&xgcvalues);
// wnd->lock_ptr = (unsigned char *)malloc(wnd->dwWidth*wnd->dwHeight<<1);
// int id = GetXSharedMemory(wnd->dwWidth*wnd->dwHeight<<1);
// if(id<0)
// {
// wnd->bHaveSharedMemory = false;
// wnd->shmInfo.shmaddr = NULL;
// }else
// {
// // attach
// wnd->shmInfo.shmid = id;
// wnd->shmInfo.shmaddr = (char *)shmat(id, 0, 0);
// }
// fprintf(stdout,"Draw: %s shared memory\n",(wnd->bHaveSharedMemory)?"Using":"Not Using");
// Initial clear
unsigned char *lock_ptr;
int lock_pitch;
if (LnxDraw_LockSurface(wnd, 0, 0, wnd->dwWidth - 1, wnd->dwHeight - 1, &lock_ptr, &lock_pitch)) {
memset(lock_ptr, 0, wnd->dwWidth * wnd->dwHeight << 1);
LnxDraw_UnlockSurface(wnd, lock_ptr);
}
return 0;
}
//////////////////////////
// LnxDraw_DestroyWindow
//////////////////////////
// Closes and deletes a window
//
// Returns:
// 0 : no error
// -1 : invalid parameter
int LnxDraw_DestroyWindow(LnxWindow *handle) {
return -1;
int i;
LnxWindow *wnd = NULL;
for (i = 0; i < NumWindows; i++) {
if (WindowList[i] == handle) {
wnd = handle;
break;
}
}
if (!wnd)
return -1;
WindowList[i] = WindowList[NumWindows - 1];
WindowList = (LnxWindow **)realloc(WindowList, sizeof(LnxWindow *) * (NumWindows - 1));
NumWindows--;
/*
if(wnd->shmInfo.shmaddr)
{
// Release shared memory.
shmdt(wnd->shmInfo.shmaddr);
shmctl(wnd->shmInfo.shmid, IPC_RMID, 0);
}
if(!wnd->WindowPreCreated)
{
// Do what we need to do to close the window
XDestroyWindow(lpDisplay,wnd->wWindow);
XFree(wnd->lpSizeHints);
XFree(wnd->lpWmHints);
XFree(wnd->lpClassHints);
}
free(wnd->lock_ptr);
*/
if (wnd->fullScreen) {
SDL_WM_ToggleFullScreen(wnd->surface);
SDL_ShowCursor(1);
} // if
free(wnd);
return 0;
}
////////////////////////
// LnxDraw_LockSurface
////////////////////////
// Locks the window surface, giving you a pointer to write data to
//
// Returns:
// true : success
// false : error
bool LnxDraw_LockSurface(LnxWindow *wnd, unsigned x1, unsigned y1, unsigned x2, unsigned y2, unsigned char **ptr,
int *pitch) {
return -1;
if (!wnd || !ptr || !pitch)
return false;
if (wnd->bLocked)
return false;
wnd->bLocked = true;
*pitch = wnd->dwWidth << 1;
/*
int w,h;
if(x2<x1){ int t; t = x2; x2=x1; x1=t;}
if(y2<y2){ int t; t = y2; y2=y1; y1=t;}
w = x2 - x1 + 1;
h = y2 - y1 + 1;
try_no_shared:
if(wnd->bHaveSharedMemory)
{
wnd->lpImage =
XShmCreateImage(lpDisplay,wnd->lpXvisual,wnd->viVisualInfo.depth,ZPixmap,0,&wnd->shmInfo,wnd->dwWidth,h);
wnd->lpImage->bitmap_bit_order = LSBFirst;
wnd->lpImage->byte_order = LSBFirst;
wnd->lpImage->bits_per_pixel = 16;
wnd->lpImage->bytes_per_line = wnd->dwWidth<<1;
wnd->lpImage->red_mask = wnd->lpXvisual->red_mask;
wnd->lpImage->green_mask = wnd->lpXvisual->green_mask;
wnd->lpImage->blue_mask = wnd->lpXvisual->blue_mask;
wnd->lpImage->data = wnd->shmInfo.shmaddr;
if(!wnd->lpImage->data)
{
wnd->bHaveSharedMemory = false;
//fprintf(stderr,"Shared Memory: Invalid Memory\n");
goto try_no_shared;
}
// attach the X server to it
wnd->shmInfo.readOnly = False;
if(!XShmAttach(lpDisplay,&wnd->shmInfo))
{
wnd->bHaveSharedMemory = false;
//fprintf(stderr,"Shared Memory: Unable to attach to server\n");
goto try_no_shared;
}
}else
{
wnd->lpImage = XCreateImage(lpDisplay,wnd->lpXvisual,wnd->viVisualInfo.depth,ZPixmap,0,(char
*)wnd->lock_ptr,wnd->dwWidth,h,16,0); wnd->lpImage->bitmap_bit_order = LSBFirst; wnd->lpImage->byte_order = LSBFirst;
wnd->lpImage->bits_per_pixel = 16;
wnd->lpImage->bytes_per_line = wnd->dwWidth<<1;
wnd->lpImage->red_mask = wnd->lpXvisual->red_mask;
wnd->lpImage->green_mask = wnd->lpXvisual->green_mask;
wnd->lpImage->blue_mask = wnd->lpXvisual->blue_mask;
wnd->lpImage->data = (char *)wnd->lock_ptr;
}
*ptr = (unsigned char *)wnd->lpImage->data;
wnd->lock_x = x1;
wnd->lock_y = y1;
wnd->lock_w = w;
wnd->lock_h = h;
*/
if (SDL_MUSTLOCK(wnd->surface)) {
if (SDL_LockSurface(wnd->surface) < 0)
return (false);
} // if
int imgHeight = y2 - y1;
int linesDown = (wnd->dwHeight - imgHeight) / 2;
*ptr = ((unsigned char *)wnd->surface->pixels) + ((wnd->surface->format->BytesPerPixel * wnd->dwWidth) * linesDown);
return true;
}
//////////////////////////
// LnxDraw_UnlockSurface
//////////////////////////
// Unlocks the window surface, blitting the buffer
//
void LnxDraw_UnlockSurface(LnxWindow *wnd, unsigned char *ptr) {
return;
if (!wnd->bLocked)
return;
bool still_have_shared = true;
wnd->bLocked = false;
/*
// blit the region
if(wnd->bHaveSharedMemory)
{
if(!XShmPutImage(lpDisplay,wnd->wWindow,wnd->m_GC,wnd->lpImage,0,0,wnd->lock_x,wnd->lock_y,wnd->lock_w,wnd->lock_h,True))
{
//fprintf(stderr,"XShmPutImage: blit failed\n");
wnd->bHaveSharedMemory = false;
}
// Detach from X server
XShmDetach(lpDisplay,&wnd->shmInfo);
}else
{
// draw the image
if(XPutImage(lpDisplay,wnd->wWindow,wnd->m_GC,wnd->lpImage,0,0,wnd->lock_x,wnd->lock_y,wnd->lock_w,wnd->lock_h))
{
//fprintf(stderr,"XPutImage: blit failed\n");
}
}
// sync up with server
XSync(lpDisplay, False);
// Kill the memory
wnd->lpImage->data = NULL;//make sure we don't delete our memory here
XDestroyImage(wnd->lpImage);
*/
if (SDL_MUSTLOCK(wnd->surface))
SDL_UnlockSurface(wnd->surface);
SDL_UpdateRect(wnd->surface, 0, 0, wnd->dwWidth, wnd->dwHeight);
#ifdef __DUMP_MVE_TO_DISK
static unsigned long framenum = 0;
char filename[100];
snprintf(filename, sizeof(filename), "./mve/frame%lu.bmp", framenum);
SDL_SaveBMP(wnd->surface, filename);
framenum++;
#endif
}
////////////////////////////
/// LnxDraw_Blit
////////////////////////////
// Blits a buffer to the window
//
// Returns:
// 0 : no error
// -1 : invalid parameter
// -2 : unknown error
int LnxDraw_Blit(LnxWindow *wnd, unsigned char *ptr, unsigned int x, unsigned int y, unsigned int w, unsigned int h) {
return 0;
/*
if(!wnd || !ptr)
return -1;
//blt to pixmap
if(wnd->viVisualInfo.depth==16)
{
//check for best case
if(x==0 && y==0 && w==wnd->dwWidth && h==wnd->dwHeight)
{
BltBuffer16ToPixMap16((unsigned char *)wnd->lpImage->data,ptr,wnd->dwWidth,wnd->dwHeight);
}else
{
int num_rows_to_blit;
int start_row,pitch;
unsigned char *curr_dest,*curr_src;
pitch = wnd->dwWidth<<1;
curr_dest = (unsigned char *)wnd->lpImage->data + (y*pitch) + (x<<1);
curr_src = ptr;
num_rows_to_blit = h;
// blit away
while(num_rows_to_blit>0)
{
BltBuffer16ToPixMap16(curr_dest,curr_src,w,1);
num_rows_to_blit--;
curr_dest += pitch;
curr_src += pitch;
}
}
}else
{
return -2;
}
//Update window
if(wnd->bHaveSharedMemory)
{
if(!XShmPutImage(lpDisplay,wnd->wWindow,wnd->m_GC,wnd->lpImage,0, 0, 0,
0,wnd->dwWidth,wnd->dwHeight,True))
{
return -2;
}
}else
{
// draw the image
XPutImage(lpDisplay,wnd->wWindow,wnd->m_GC,wnd->lpImage,0, 0,0,0,wnd->dwWidth,wnd->dwHeight);
}
// sync up with server
XSync(lpDisplay, False);
return 0;
*/
}
////////////////////////
// LnxDraw_GetRGBMasks
////////////////////////
// Returns the RGB masks for the display
//
// Returns:
// 0 : no error
// -1 : invalid parameters
int LnxDraw_GetRGBMasks(LnxWindow *wnd, unsigned int *r, unsigned int *g, unsigned int *b) {
return -1;
if (!wnd || !r || !g || !b)
return -1;
/*
*r = wnd->lpImage->red_mask;
*g = wnd->lpImage->green_mask;
*b = wnd->lpImage->blue_mask;
*/
SDL_PixelFormat *pixelFmt = wnd->surface->format;
*r = pixelFmt->Rmask;
*g = pixelFmt->Gmask;
*b = pixelFmt->Bmask;
return (0);
}
static int GetXSharedMemory(int size) {
/*
int key = (14<<24)|(70<<16)|(81<<8)|49;
struct shmid_ds shminfo;
int minsize = 640*480;
int id;
int rc;
int num_try=5;
do
{
//try to get the id
id = shmget((key_t) key, minsize,0x1FF);
if(id!=-1)
{
rc = shmctl(id, IPC_STAT, &shminfo);
if(!rc)
{
if(shminfo.shm_nattch)
{
key++;
}else
{
if(getuid()==shminfo.shm_perm.cuid)
{
rc = shmctl(id,IPC_RMID,0);
if(!rc)
{
//fprintf(stderr,"Shared Memory: Stale memory killed\n");
}
else
{
//fprintf(stderr,"Shared Memory: Unable to kill stale
memory\n"); return -2;
}
id = shmget((key_t)key,size,IPC_CREAT|0x1FF);
if(id==-1)
{
//fprintf(stderr,"Shared Memory: Unable to create shared
memory block (size=%d)\n",size); return -2;
}
rc=shmctl(id,IPC_STAT,&shminfo);
break;
}
if(size>=shminfo.shm_segsz)
{
//fprintf(stderr,"Shared Memory: Using User %d's shared
memory\n",shminfo.shm_cpid); break; }else
{
//fprintf(stderr,"Shared Memory: Stale memory (User %d, Key=0x%x) is
too small\n",shminfo.shm_cpid,key); key++;
}
}
}else
{
//fprintf(stderr,"Shared Memory: Unable to read stats on 0x%x\n",key);
return -2;
}
}else
{
id = shmget((key_t)key,size,IPC_CREAT|0x1FF);
if(id==-1)
{
//fprintf(stderr,"Shared Memory: Unable to get shared memory (error = %d)\n",errno);
return -2;
}
break;
}
}while (--num_try);
if(num_try == 0)
{
//fprintf(stderr,"Shared Memory: Unable to get shared memory (too many stale shared memory
segments)\n");
}
return id;
*/
return (0);
}
inline void BltBuffer32ToPixMap16(unsigned char *pixmap, unsigned char *buffer, int width, int height) {
unsigned char *data;
unsigned short int l;
int r, g, b, a;
unsigned int c;
data = (unsigned char *)pixmap;
for (l = height * width; l > 0; l--) {
c = *(unsigned int *)buffer;
a = ((c & 0xff000000) >> 24);
r = ((c & 0x00ff0000) >> 16);
g = ((c & 0x0000ff00) >> 8);
b = (c & 0x000000ff);
if (a)
*(unsigned int *)data = (((r >> 3) << 10) + ((g >> 3) << 5) + (b >> 3));
data += 2;
buffer += 4;
}
}
inline void BltBuffer16ToPixMap16(unsigned char *pixmap, unsigned char *buffer, int width, int height) {
unsigned char *data;
data = (unsigned char *)pixmap;
memcpy(data, buffer, (width * height) << 1);
}
inline void BltBuffer32ToPixMap24(unsigned char *pixmap, unsigned char *buffer, int width, int height) {
unsigned char *data;
unsigned short int l;
int r, g, b, a;
unsigned int c;
data = (unsigned char *)pixmap;
for (l = height * width; l > 0; l--) {
c = *(unsigned int *)buffer;
a = ((c & 0xff000000) >> 24);
r = ((c & 0x00ff0000) >> 16);
g = ((c & 0x0000ff00) >> 8);
b = (c & 0x000000ff);
if (a)
*(unsigned long *)data = ((r << 16) + (g << 8) + b);
data += 4;
buffer += 4;
}
}
inline void BltBuffer16ToPixMap24(unsigned char *pixmap, unsigned char *buffer, int width, int height) {
unsigned char *data;
unsigned short int l;
int r, g, b, a;
unsigned short c;
data = (unsigned char *)pixmap;
for (l = height * width; l > 0; l--) {
c = *(unsigned short *)buffer;
a = ((c & 0x8000) >> 15);
r = ((c & 0x7C00) >> 10);
g = ((c & 0x03E0) >> 5);
b = (c & 0x001F);
if (a)
*(unsigned long *)data = ((r << 19) + (g << 11) + (b << 3));
data += 4;
buffer += 2;
}
}