summaryrefslogtreecommitdiff
path: root/linux/gfx.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux/gfx.c')
-rw-r--r--linux/gfx.c1013
1 files changed, 1013 insertions, 0 deletions
diff --git a/linux/gfx.c b/linux/gfx.c
new file mode 100644
index 0000000..01c7f4a
--- /dev/null
+++ b/linux/gfx.c
@@ -0,0 +1,1013 @@
+/*
+
+ viDOOM - level editor for DOOM
+
+ Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk)
+
+ 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
+
+ -------------------------------------------------------------------------
+
+ Graphics functions
+
+*/
+static const char rcs_id[]="$Id$";
+
+#include "config.h"
+#include "gfx.h"
+#include "mem.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+
+/* ---------------------------------------- VARS
+*/
+static int init=FALSE;
+
+static Window win=None;
+static Pixmap pix=None;
+static Pixmap saved=None;
+static GC gc=None;
+static Display *disp=NULL;
+static XVisualInfo visual;
+static XFontStruct *font=NULL;
+static int curr_func;
+
+#define FONTGAP 2
+#define FONTW (font->max_bounds.rbearing - \
+ font->min_bounds.lbearing)
+#define FONTH (font->max_bounds.descent + \
+ font->max_bounds.ascent + FONTGAP)
+
+static int width=0;
+static int height=0;
+
+static int fw=0;
+static int fh=0;
+
+static int mbuttons=3; /* Assume 3 for X11 */
+
+static int dirty_min_x=0;
+static int dirty_max_x=0;
+static int dirty_min_y=0;
+static int dirty_max_y=0;
+
+static char *font_names[]=
+ {
+ "6x9",
+ NULL
+ };
+
+#define DIRTY(x,y) do { \
+ dirty_min_x=MIN(x,dirty_min_x); \
+ dirty_min_y=MIN(y,dirty_min_y); \
+ dirty_max_x=MAX((x)+1,dirty_max_x); \
+ dirty_max_y=MAX((y)+1,dirty_max_y); \
+ } while(0)
+
+
+/* Internal representation used for GFX_IMAGE
+*/
+typedef struct
+ {
+ int w,h;
+ XImage *p;
+ } GfxImage;
+
+/* These are used to define the mappings between X11 scancodes and GFX keys.
+ Then the scancode can be used to directly index and array.
+*/
+static struct
+ {
+ int is_special;
+ int key;
+ } keycode_map[sizeof(KeyCode)<< CHAR_BIT];
+
+typedef struct KBuff
+ {
+ GFXKey k;
+ struct KBuff *next;
+ } KeyBuff;
+
+static KeyBuff *kb_head=NULL;
+static KeyBuff *kb_tail=NULL;
+
+static struct
+ {
+ KeySym keysym;
+ int code;
+ } key_map[]= {
+ {XK_F1, GFX_F1},
+ {XK_F2, GFX_F2},
+ {XK_F3, GFX_F3},
+ {XK_F4, GFX_F4},
+ {XK_F5, GFX_F5},
+ {XK_F6, GFX_F6},
+ {XK_F7, GFX_F7},
+ {XK_F8, GFX_F8},
+ {XK_F9, GFX_F9},
+ {XK_F10, GFX_F10},
+ {XK_F11, GFX_F11},
+ {XK_F12, GFX_F12},
+ {XK_Escape, GFX_ESC},
+ {XK_Insert, GFX_INSERT},
+ {XK_Home, GFX_HOME},
+ {XK_Page_Up, GFX_PGUP},
+ {XK_Delete, GFX_DELETE},
+ {XK_End, GFX_END},
+ {XK_Page_Down, GFX_PGDN},
+ {XK_Up, GFX_UP},
+ {XK_Down, GFX_DOWN},
+ {XK_Left, GFX_LEFT},
+ {XK_Right, GFX_RIGHT},
+ {XK_Return, GFX_ENTER},
+ {XK_BackSpace, GFX_BACKSPACE},
+ {XK_Tab, GFX_TAB},
+
+ {XK_VoidSymbol, -1},
+ };
+
+/* Types and vars for event handling
+*/
+typedef enum
+ {
+ X11_GFXnone=0,
+ X11_GFXmouse=1,
+ X11_GFXmouseMove=2,
+ X11_GFXkey=4,
+ X11_GFXall=4|2|1
+ } EventType;
+
+typedef enum
+ {
+ X11_GFXpress, X11_GFXrelease, X11_GFXmove
+ } MouseEventType;
+
+static int mouse_b=0;
+static int mouse_x=0;
+static int mouse_y=0;
+static int is_shift=FALSE;
+static int is_ctrl=FALSE;
+static int is_alt=FALSE;
+
+
+/* ---------------------------------------- PRIVATE FUNCTIONS
+*/
+
+static unsigned long getXCol(int col)
+{
+ XColor pix;
+
+ pix.red=(col&0xff0000)>>8;
+ pix.green=(col&0xff00);
+ pix.blue=(col&0xff)<<8;
+ pix.flags=DoRed|DoBlue|DoGreen;
+
+ XAllocColor(disp,DefaultColormap(disp,DefaultScreen(disp)),&pix);
+
+ return pix.pixel;
+}
+
+
+static void setXCol(int col)
+{
+ static int last_col=-1;
+
+ if (col==last_col)
+ return;
+
+ last_col=col;
+
+ XSetForeground(disp,gc,getXCol(col));
+}
+
+
+static void KeyBuffAdd(int code, char ascii, int shift, int ctrl, int alt)
+{
+ KeyBuff *key;
+
+ key=Grab(sizeof *key);
+
+ key->k.code=code;
+ key->k.ascii=ascii;
+ key->k.shift=shift;
+ key->k.ctrl=ctrl;
+ key->k.alt=alt;
+ key->next=NULL;
+
+ if (!kb_head)
+ kb_head=kb_tail=key;
+ else
+ {
+ kb_tail->next=key;
+ kb_tail=key;
+ }
+}
+
+
+static void GetKey(GFXKey *key)
+{
+ if (kb_head)
+ {
+ KeyBuff *k=kb_head;
+
+ *key=k->k;
+
+ if (!(kb_head=k->next))
+ kb_tail=NULL;
+
+ Release(k);
+ }
+}
+
+
+static int HandleKey(XKeyEvent *key)
+{
+ int code;
+ char ascii;
+
+ if (keycode_map[key->keycode].is_special)
+ {
+ code=keycode_map[key->keycode].key;
+ ascii=0;
+ }
+ else
+ {
+ char buff[2]="";
+
+ code=GFX_ASCII;
+
+ XLookupString(key,buff,2,NULL,NULL);
+
+ if (!isprint(buff[0]))
+ return FALSE;
+
+ ascii=buff[0];
+ }
+
+ KeyBuffAdd(code,ascii,
+ key->state&ShiftMask,
+ key->state&ControlMask,
+ key->state&Mod1Mask);
+
+ return TRUE;
+}
+
+
+static void HandleMouse(int x, int y, int b, MouseEventType type,
+ unsigned int state)
+{
+ int mask;
+
+ mouse_x=x;
+ mouse_y=y;
+
+ if (type!=X11_GFXmove)
+ {
+ switch(b)
+ {
+ case Button1:
+ mask=GFX_BUTLEFT;
+ break;
+ case Button2:
+ mask=GFX_BUTMIDDLE;
+ break;
+ case Button3:
+ mask=GFX_BUTRIGHT;
+ break;
+ case Button4:
+ mask=GFX_MSWHEELUP;
+ break;
+ case Button5:
+ mask=GFX_MSWHEELDOWN;
+ break;
+ default:
+ mask=0;
+ break;
+ }
+
+ if (type==X11_GFXpress)
+ mouse_b|=mask;
+ else
+ mouse_b&=~mask;
+ }
+
+ is_shift=state&ShiftMask;
+ is_ctrl=state&ControlMask;
+ is_alt=state&Mod1Mask;
+}
+
+
+static EventType Wait(EventType etype, int wait)
+{
+ XEvent ev;
+
+ while(wait || XPending(disp))
+ {
+ XNextEvent(disp,&ev);
+
+ switch(ev.type)
+ {
+ case KeyPress:
+ if (HandleKey(&ev.xkey) && (etype & X11_GFXkey))
+ return X11_GFXkey;
+
+ break;
+
+ case ButtonPress:
+ HandleMouse(ev.xbutton.x,ev.xbutton.y,ev.xbutton.button,
+ X11_GFXpress,ev.xbutton.state);
+
+ if (etype & X11_GFXmouse)
+ return X11_GFXmouse;
+
+ break;
+
+ case ButtonRelease:
+ HandleMouse(ev.xbutton.x,ev.xbutton.y,ev.xbutton.button,
+ X11_GFXrelease,ev.xbutton.state);
+
+ if (etype & X11_GFXmouse)
+ return X11_GFXmouse;
+
+ break;
+
+ case MotionNotify:
+ HandleMouse(ev.xbutton.x,ev.xbutton.y,0,
+ X11_GFXmove,ev.xbutton.state);
+
+ if (etype & X11_GFXmouseMove)
+ return X11_GFXmouseMove;
+
+ break;
+
+ case Expose:
+ DIRTY(ev.xexpose.x,ev.xexpose.y);
+ DIRTY(ev.xexpose.x+ev.xexpose.width,
+ ev.xexpose.y+ev.xexpose.height);
+
+ if (ev.xexpose.count==0)
+ GFX_redraw();
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return X11_GFXnone;
+}
+
+
+
+/* ---------------------------------------- 'PRIVATE' EXPORTED FUNCTIONS
+*/
+void VIDOOM_GET_GFX_VARS(Display **ret_disp, Window *ret_win, Pixmap *ret_pix,
+ GC *ret_gc)
+{
+ *ret_disp=disp;
+ *ret_win=win;
+ *ret_pix=pix;
+ *ret_gc=gc;
+}
+
+
+void VIDOOM_GFX_FORCE_REDRAW(void)
+{
+ dirty_min_x=0;
+ dirty_max_x=width-1;
+ dirty_min_y=0;
+ dirty_max_y=width-1;
+
+ GFX_redraw();
+}
+
+
+void VIDOOM_GFX_SAVE_DISPLAY(void)
+{
+ if (curr_func!=GXcopy)
+ XSetFunction(disp,gc,GXcopy);
+
+ XCopyArea(disp,pix,saved,gc,0,0,width,height,0,0);
+}
+
+
+void VIDOOM_GFX_RESTORE_DISPLAY(void)
+{
+ XCopyArea(disp,saved,pix,gc,0,0,width,height,0,0);
+ VIDOOM_GFX_FORCE_REDRAW();
+
+ if (curr_func!=GXcopy)
+ XSetFunction(disp,gc,curr_func);
+}
+
+
+Pixmap VIDOOM_GFX_SAVE_UNDER(int x, int y, int w, int h)
+{
+ Pixmap p;
+
+ p=XCreatePixmap(disp,win,w,h,visual.depth);
+ XCopyArea(disp,pix,p,gc,x,y,w,h,0,0);
+
+ return p;
+}
+
+
+void VIDOOM_GFX_RESTORE_UNDER(Pixmap p, int x, int y, int w, int h)
+{
+ DIRTY(x,y);
+ DIRTY(x+w,y+h);
+ XCopyArea(disp,p,pix,gc,0,0,w,h,x,y);
+ XFreePixmap(disp,p);
+}
+
+
+/* This is done in a *very* bad fashion....
+*/
+void VIDOOM_GFX_WAIT_FOR(pid_t pid, int *status)
+{
+ XEvent ev;
+
+ while(TRUE)
+ {
+ while (XPending(disp))
+ {
+ XNextEvent(disp,&ev);
+
+ if (ev.type==Expose)
+ {
+ DIRTY(ev.xexpose.x,ev.xexpose.y);
+ DIRTY(ev.xexpose.x+ev.xexpose.width,
+ ev.xexpose.y+ev.xexpose.height);
+
+ if (ev.xexpose.count==0)
+ GFX_redraw();
+ }
+ }
+
+ if (waitpid(pid,status,WNOHANG))
+ return;
+
+ /* Now, this is nasty...
+ */
+ usleep(10000);
+ }
+}
+
+
+/* ---------------------------------------- EXPORTED FUNCTIONS
+*/
+void GFX_init(void)
+{
+ if (!init)
+ {
+ int f;
+
+ init=TRUE;
+
+ /* Open display and find an appropriate visual
+ */
+ if (!(disp=XOpenDisplay(NULL)))
+ GFX_exit(EXIT_FAILURE,"Couldn't open X display\n");
+
+ if (!XMatchVisualInfo(disp,DefaultScreen(disp),32,TrueColor,&visual) &&
+ !XMatchVisualInfo(disp,DefaultScreen(disp),24,TrueColor,&visual) &&
+ !XMatchVisualInfo(disp,DefaultScreen(disp),16,TrueColor,&visual) &&
+ !XMatchVisualInfo(disp,DefaultScreen(disp),15,TrueColor,&visual))
+ GFX_exit(EXIT_FAILURE,"Couldn't find a TrueColor visual\n");
+
+ f=0;
+ while(font_names[f] && !font)
+ font=XLoadQueryFont(disp,font_names[f++]);
+
+ if (!font)
+ {
+ font=XLoadQueryFont(disp,"fixed");
+
+ if (!font)
+ GFX_exit(EXIT_FAILURE,"Couldn't even load font fixed\n");
+ }
+
+ fw=FONTW;
+ fh=FONTH;
+
+ for(f=0;f<(sizeof(KeyCode)<< CHAR_BIT);f++)
+ keycode_map[f].is_special=FALSE;
+
+ f=0;
+
+ while(key_map[f].keysym!=XK_VoidSymbol)
+ {
+ KeyCode code;
+
+ code=XKeysymToKeycode(disp,key_map[f].keysym);
+
+ keycode_map[code].is_special=TRUE;
+ keycode_map[code].key=key_map[f].code;
+
+ f++;
+ }
+ }
+}
+
+
+void GFX_close(void)
+{
+ if (gc!=None)
+ {
+ XFreeGC(disp,gc);
+ gc=None;
+ }
+
+ if (pix!=None)
+ {
+ XFreePixmap(disp,pix);
+ pix=None;
+ }
+
+ if (saved!=None)
+ {
+ XFreePixmap(disp,saved);
+ saved=None;
+ }
+
+ if (win!=None)
+ {
+ XDestroyWindow(disp,win);
+ win=None;
+ }
+}
+
+
+void GFX_open(int w,int h)
+{
+ XGCValues gc_val;
+ XSizeHints hint;
+
+ if (!init)
+ GFX_exit(EXIT_FAILURE,"GFX_Open() called before GFX_init()\n");
+
+ height=h;
+ width=w;
+
+ win=XCreateWindow(disp,DefaultRootWindow(disp),
+ 0,0,width,height,0,
+ visual.depth,
+ InputOutput,
+ visual.visual,
+ 0,NULL);
+
+ XSelectInput
+ (disp,win,
+ KeyPressMask|ButtonPressMask|ButtonReleaseMask|
+ PointerMotionMask|ExposureMask);
+
+ XStoreName(disp,win,"viDOOM " VIDOOMVER VIDOOMRELEASE);
+
+ hint.width=hint.min_width=hint.max_width=width;
+ hint.height=hint.min_height=hint.max_height=height;
+ hint.flags=PSize|PMinSize|PMaxSize;
+
+ XSetWMNormalHints(disp,win,&hint);
+
+ pix=XCreatePixmap(disp,win,width,height,visual.depth);
+ saved=XCreatePixmap(disp,win,width,height,visual.depth);
+
+ curr_func=gc_val.function=GXcopy;
+ gc_val.plane_mask=AllPlanes;
+ gc_val.line_width=0;
+ gc_val.line_style=LineSolid;
+ gc_val.graphics_exposures=False;
+ gc_val.foreground=WhitePixel(disp,DefaultScreen(disp));
+ gc_val.background=BlackPixel(disp,DefaultScreen(disp));
+
+ gc=XCreateGC(disp,pix,
+ GCFunction|GCPlaneMask|GCLineWidth|GCForeground|GCBackground|
+ GCLineStyle|GCGraphicsExposures,
+ &gc_val);
+
+ GFX_clear(BLACK);
+ GFX_redraw();
+
+ XMapWindow(disp,win);
+}
+
+
+void GFX_clear(int col)
+{
+ if (curr_func!=GXcopy)
+ XSetFunction(disp,gc,GXcopy);
+
+ setXCol(col);
+ XFillRectangle(disp,pix,gc,0,0,width,height);
+
+ if (curr_func!=GXcopy)
+ XSetFunction(disp,gc,curr_func);
+
+ dirty_min_x=0;
+ dirty_max_x=width;
+ dirty_min_y=0;
+ dirty_max_y=height;
+}
+
+
+void GFX_redraw(void)
+{
+ if (dirty_max_x<dirty_min_x)
+ return;
+
+ if (curr_func!=GXcopy)
+ XSetFunction(disp,gc,GXcopy);
+
+ dirty_min_x=MAX(0,dirty_min_x);
+ dirty_min_y=MAX(0,dirty_min_y);
+ dirty_max_x=MIN(width,dirty_max_x);
+ dirty_max_y=MIN(height,dirty_max_y);
+
+ XCopyArea(disp,pix,win,gc,
+ dirty_min_x,dirty_min_y,
+ dirty_max_x-dirty_min_x+1,dirty_max_y-dirty_min_y+1,
+ dirty_min_x,dirty_min_y);
+
+ if (curr_func!=GXcopy)
+ XSetFunction(disp,gc,curr_func);
+
+ dirty_max_x=0;
+ dirty_min_x=width-1;
+ dirty_max_y=0;
+ dirty_min_y=width-1;
+
+ XFlush(disp);
+}
+
+
+void GFX_line(int x1,int y1,int x2,int y2,int col)
+{
+ DIRTY(x1,y1);
+ DIRTY(x2,y2);
+
+ setXCol(col);
+ XDrawLine(disp,pix,gc,x1,y1,x2,y2);
+}
+
+
+void GFX_plot(int x,int y,int col)
+{
+ DIRTY(x,y);
+
+ setXCol(col);
+ XDrawPoint(disp,pix,gc,x,y);
+}
+
+
+void GFX_circle(int x,int y,int r,int col)
+{
+ DIRTY(x-r-1,y-r-1);
+ DIRTY(x+r+1,y+r+1);
+
+ setXCol(col);
+ XDrawArc(disp,pix,gc,x-r,y-r,r*2,r*2,0,64*360);
+}
+
+
+void GFX_fcircle(int x,int y,int r,int col)
+{
+ DIRTY(x-r-1,y-r-1);
+ DIRTY(x+r+1,y+r+1);
+
+ setXCol(col);
+ XFillArc(disp,pix,gc,x-r,y-r,r*2,r*2,0,64*360);
+}
+
+
+void GFX_rect(int x,int y,int w,int h,int col)
+{
+ if (w==0 && h==0)
+ {
+ GFX_plot(x,y,col);
+ return;
+ }
+
+ w-=SGN(w);
+ h-=SGN(h);
+
+ if (w<0)
+ {
+ x+=w;
+ w=-w;
+ }
+
+ if (h<0)
+ {
+ y+=h;
+ h=-h;
+ }
+
+ DIRTY(x,y);
+ DIRTY(x+w,y+h);
+
+ setXCol(col);
+ XDrawRectangle(disp,pix,gc,x,y,w,h);
+}
+
+
+void GFX_frect(int x,int y,int w,int h,int col)
+{
+ if (w==0 && h==0)
+ {
+ GFX_plot(x,y,col);
+ return;
+ }
+
+ if (w<0)
+ {
+ x+=(w+1);
+ w=-w;
+ }
+
+ if (h<0)
+ {
+ y+=(h+1);
+ h=-h;
+ }
+
+ DIRTY(x,y);
+ DIRTY(x+w,y+h);
+
+ setXCol(col);
+ XFillRectangle(disp,pix,gc,x,y,w,h);
+}
+
+
+void GFX_set_XOR_mode(void)
+{
+ XSetFunction(disp,gc,curr_func=GXxor);
+}
+
+
+void GFX_clear_XOR_mode(void)
+{
+ XSetFunction(disp,gc,curr_func=GXcopy);
+}
+
+
+void GFX_print(int x,int y,int col,char *fmt,...)
+{
+ char s[1024];
+ va_list va;
+ size_t len;
+
+ va_start(va,fmt);
+ vsprintf(s,fmt,va);
+ va_end(va);
+
+ len=strlen(s);
+
+ y+=fh-FONTGAP;
+
+ DIRTY(x,y);
+ DIRTY(x+fw*strlen(s),y+fh);
+
+ setXCol(col);
+ XDrawString(disp,pix,gc,x,y,s,len);
+}
+
+
+int GFX_fh(void)
+{
+ return fh;
+}
+
+
+int GFX_fw(void)
+{
+ return fw;
+}
+
+
+int GFX_mouse_buttons(void)
+{
+ return mbuttons;
+}
+
+
+int GFX_mouse(int *x,int *y)
+{
+ Wait(X11_GFXall,FALSE);
+
+ if (x)
+ *x=mouse_x;
+
+ if (y)
+ *y=mouse_y;
+
+ return mouse_b;
+}
+
+
+void GFX_waitkey(GFXKey *key)
+{
+ GFXKey ev;
+
+ Wait(X11_GFXkey,TRUE);
+ GetKey(key ? key:&ev);
+}
+
+
+int GFX_key(GFXKey *key)
+{
+ Wait(X11_GFXall,FALSE);
+
+ if (kb_head)
+ {
+ GetKey(key);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+void GFX_bounce(void)
+{
+ GFXKey d;
+
+ while (kb_head)
+ GetKey(&d);
+
+ mouse_b=0;
+}
+
+
+void GFX_await_input(GFXEvent *ev)
+{
+ switch(Wait(X11_GFXkey|X11_GFXmouse,TRUE))
+ {
+ case X11_GFXkey:
+ GetKey(&(ev->key));
+ ev->type=GFX_KEY_EVENT;
+ break;
+
+ case X11_GFXmouse:
+ ev->type=GFX_MOUSE_EVENT;
+ ev->mouse.shift=is_shift;
+ ev->mouse.ctrl=is_ctrl;
+ ev->mouse.alt=is_alt;
+ ev->mouse.x=mouse_x;
+ ev->mouse.y=mouse_y;
+ ev->mouse.b=mouse_b;
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+void GFX_await_input_full(GFXEvent *ev)
+{
+ switch(Wait(X11_GFXall,TRUE))
+ {
+ case X11_GFXkey:
+ GetKey(&(ev->key));
+ ev->type=GFX_KEY_EVENT;
+ break;
+
+ case X11_GFXmouse:
+ case X11_GFXmouseMove:
+ ev->type=GFX_MOUSE_EVENT;
+ ev->mouse.shift=is_shift;
+ ev->mouse.ctrl=is_ctrl;
+ ev->mouse.alt=is_alt;
+ ev->mouse.x=mouse_x;
+ ev->mouse.y=mouse_y;
+ ev->mouse.b=mouse_b;
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+void GFX_exit(int code,char *fmt,...)
+{
+ va_list va;
+
+ va_start(va,fmt);
+ vfprintf(stderr,fmt,va);
+ va_end(va);
+
+ /* Be lazy, and leave the might of Unix to clear up, though may as well
+ let X know.
+ */
+ if (disp)
+ XCloseDisplay(disp);
+
+ exit(code);
+}
+
+
+GFX_IMAGE GFX_create_image(GFX_BITMAP *bm)
+{
+ GfxImage i;
+ char *data;
+ int x,y;
+ unsigned long pal[256];
+
+ data=Grab(bm->w*bm->h*4);
+
+ i.p=XCreateImage(disp,visual.visual,visual.depth,
+ ZPixmap,0,data,bm->w,bm->h,32,4*bm->w);
+
+ if (!i.p)
+ {
+ Release(data);
+ return NULL;
+ }
+
+ for(x=0;x<256;x++)
+ pal[x]=getXCol(bm->pal[x]);
+
+ i.w=bm->w;
+ i.h=bm->h;
+
+ for(x=0;x<bm->w;x++)
+ for(y=0;y<bm->h;y++)
+ XPutPixel(i.p,x,y,pal[*(bm->data+x+y*bm->w)]);
+
+ return Copy(&i,sizeof i);
+}
+
+
+void GFX_destroy_image(GFX_IMAGE img)
+{
+ GfxImage *p=img;
+
+ if (p)
+ {
+ XDestroyImage(p->p);
+ Release(p);
+ }
+}
+
+
+void GFX_draw_image(GFX_IMAGE i, int x, int y)
+{
+ GfxImage *p=i;
+
+ if (p)
+ {
+ if (curr_func!=GXcopy)
+ XSetFunction(disp,gc,GXcopy);
+
+ XPutImage(disp,pix,gc,p->p,0,0,x,y,p->w,p->h);
+
+ DIRTY(x,y);
+ DIRTY(x+p->w,y+p->h);
+
+ if (curr_func!=GXcopy)
+ XSetFunction(disp,gc,curr_func);
+ }
+}
+
+
+void GFX_fill_screen(GFX_IMAGE i)
+{
+ GfxImage *p=i;
+
+ if (p)
+ GFX_draw_image(i,(width-p->w)/2,(height-p->h)/2);
+}
+
+
+void GFX_save_screen(char *path)
+{
+}
+
+
+/* END OF FILE */