/* 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 #include #include #include #include #include #include #include #include #include #include /* ---------------------------------------- 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_xkey)); 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;xw;x++) for(y=0;yh;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 */