From a9022b5972dc49d86f617a27940fafe9c4d0e7e7 Mon Sep 17 00:00:00 2001 From: Ian C Date: Thu, 9 Jun 2011 13:46:28 +0000 Subject: Initial import of (very old) vidoom sources. --- linux/file.c | 103 ++ linux/gfx.c | 1013 +++++++++++++++ linux/main.c | 33 + linux/mem.c | 107 ++ linux/platgui.c | 3703 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ linux/runcmd.c | 82 ++ linux/vstring.c | 48 + 7 files changed, 5089 insertions(+) create mode 100644 linux/file.c create mode 100644 linux/gfx.c create mode 100644 linux/main.c create mode 100644 linux/mem.c create mode 100644 linux/platgui.c create mode 100644 linux/runcmd.c create mode 100644 linux/vstring.c (limited to 'linux') diff --git a/linux/file.c b/linux/file.c new file mode 100644 index 0000000..6094021 --- /dev/null +++ b/linux/file.c @@ -0,0 +1,103 @@ +/* + + 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 + + ------------------------------------------------------------------------- + + File system interfaces + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" +#include + + +/* ---------------------------------------- EXPORTED FUNCS +*/ +char *Pwd(void) +{ + static char s[PATH_MAX]; + + getcwd(s,PATH_MAX); + + return s; +} + +void Cd(char *path) +{ + chdir(path); +} + +char *Dirname(char *path) +{ + static char s[PATH_MAX]; + char *p; + + strcpy(s,path); + + p=s+strlen(s)-1; + + while((*p)&&(p>s)) + if (*p=='/') + *(p+1)=0; + else + p--; + + return s; +} + +char *Basename(char *path) +{ + static char s[PATH_MAX]; + char *p; + + strcpy(s,path); + + p=s+strlen(s)-1; + + while(p>s) + if (*p=='/') + return p+1; + else + p--; + + return(s); +} + +int FileExists(char *path) +{ + FILE *fp; + + if ((fp=fopen(path,"r"))) + { + fclose(fp); + return TRUE; + } + else + return FALSE; +} + +int FilenamesEqual(char *path1, char *path2) +{ + return !strcasecmp(path1,path2); +} + + +/* END OF FILE */ 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 +#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 */ diff --git a/linux/main.c b/linux/main.c new file mode 100644 index 0000000..9cda67e --- /dev/null +++ b/linux/main.c @@ -0,0 +1,33 @@ +/* + + 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 + + ------------------------------------------------------------------------- + + Startup code + +*/ +static const char rcs_id[]="$Id$"; + +#include "vidoom.h" + +int main(int argc,char *argv[]) +{ + return viDOOM(argc,argv); +} diff --git a/linux/mem.c b/linux/mem.c new file mode 100644 index 0000000..a23b4ce --- /dev/null +++ b/linux/mem.c @@ -0,0 +1,107 @@ +/* + + 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 + + ------------------------------------------------------------------------- + + Memory allocation code + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" + +#include +#include + +#include "mem.h" +#include "gfx.h" + +void *FGrab(char *fn, int line, int len) +{ + char *ptr; + + if (len==0) + len=1; + + if (!(ptr=malloc(len))) + GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n" + "%s:%d Grab(%d)\n",fn,line,len); + + memset(ptr,0,len); + + return ptr; +} + + +void *FReGrab(char *fn, int line, void *ptr, int len) +{ + if (len==0) + len=1; + + if (!(ptr=realloc(ptr,len))) + GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n" + "%s:%d ReGrab(%d)\n",fn,line,len); + + return ptr; +} + + +void FRelease(char *fn, int line, void *p) +{ + free(p); +} + + +void *FCopy(char *fn, int line, void *p,int len) +{ + void *ptr; + + if (len==0) + ptr=malloc(1); + else + ptr=malloc(len); + + if (!ptr) + GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n" + "%s:%d Copy(%p,%d)\n",fn,line,p,len); + + if (len) + memcpy(ptr,p,len); + + return ptr; +} + + +char *FStrdup(char *fn, int line, char *p) +{ + char n_fn[PATH_MAX]; + char *ptr; + + if (!p) + return NULL; + + if (!(ptr=strdup(p))) + GFX_exit(EXIT_FAILURE,"Memory allocation failed!\n" + "%s:%d Stdup(%s)\n",fn,line,p); + + return ptr; +} + +/* END OF FILE */ diff --git a/linux/platgui.c b/linux/platgui.c new file mode 100644 index 0000000..5fd9c74 --- /dev/null +++ b/linux/platgui.c @@ -0,0 +1,3703 @@ +/* + + 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 + + ------------------------------------------------------------------------- + + Platform specific GUI type routines + +*/ +static const char rcs_id[]="$Id$"; + +#include "config.h" + +#include "platgui.h" +#include "gui.h" +#include "gfx.h" +#include "mem.h" +#include "file.h" +#include "ini.h" +#include "util.h" + +#include "debug.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +/* ---------------------------------------- MACROS +*/ +#define CENTRE(x,y,c,t) GFX_print((x)-strlen(t)*FW/2,y,c,"%s",t) + +#define CENTREX(x,y,w,c,t) GFX_print(((x)+(w)/2)-strlen(t)*FW/2,y,c,"%s",t) +#define CENTREY(x,y,h,c,t) GFX_print((x),((y)+(h)/2)-FH/2,c,"%s",t) +#define CENTREXY(x,y,w,h,c,t) GFX_print(((x)+(w)/2)-strlen(t)*FW/2,\ + ((y)+(h)/2)-FH/2,c,"%s",t) +#define LENGTH(t) (strlen(t)*FW) + + +/* ---------------------------------------- CONFIG +*/ +static char GUI_EDITOR[PATH_MAX]=""; + + +static INI_Table ini_conf[]= + { + {INI_STR,"linux","edit",GUI_EDITOR,NULL}, + }; + +/* ---------------------------------------- VARS +*/ +static Display *disp=NULL; +static Window win=None; +static Pixmap pix=None; +static GC gc=None; + +static int SCRW,SCRH; +static int FW,FH; + +static unsigned long hi_col; +static unsigned long mid_col; +static unsigned long lo_col; +static unsigned long txt_col; +static unsigned long shadow_col; +static unsigned long bold_col; + +static const int BEVEL=2; +static const int SMALL_BEVEL=1; + +#define LIST_SCROLLSIZE 20 +#define LIST_TEXTHEIGHT (FH+(FH>>1)) + +/* Internal representation used for GFX_IMAGE + + IMPORTANT: This *MUST* match the definition in linux/gfx.c + +*/ +typedef struct + { + int w,h; + XImage *p; + } GfxImage; + + +/* Vars for GUI_yesno_all() +*/ +static int all_pressed=FALSE; +static int all_result=FALSE; + +/* Event Vars +*/ +static int mouse_x=0; +static int mouse_y=0; +static int mouse_b=0; +static int last_mouse_b=0; +static int is_key=FALSE; +static int key=0; +static int shift=0; +static int ctrl=0; +static char ascii=0; + +/* 'Hidden' interfaces to gfx.c +*/ +extern void VIDOOM_GET_GFX_VARS(Display **ret_disp, + Window *ret_win, + Pixmap *ret_pix, + GC *ret_gc); +extern void VIDOOM_GFX_FORCE_REDRAW(void); +extern void VIDOOM_GFX_SAVE_DISPLAY(void); +extern void VIDOOM_GFX_RESTORE_DISPLAY(void); +extern Pixmap VIDOOM_GFX_SAVE_UNDER(int x, int y, int w, int h); +extern void VIDOOM_GFX_RESTORE_UNDER(Pixmap p, int x, int y, int w, int h); +extern void VIDOOM_GFX_WAIT_FOR(pid_t pid, int *status); + + +/* Types to define GUI objects. Note picklist done as an image list with + no images. +*/ +typedef enum {GuiBox, + GuiLabel, + GuiButton, + GuiText, + GuiToggle, + GuiRadio, + GuiImageList, + GuiFileList, + GuiTextEdit} ObjType; + +typedef enum {GuiKeyPress, + GuiButtonPress, + GuiWheelUp, + GuiWheelDown, + GuiDraw, + GuiExit} ObjEvent; + +typedef enum {GuiOk, + GuiDone, + GuiRedraw} CallbackReturn; + +typedef enum {GuiTextString, + GuiTextInteger, + GuiTextDouble, + GuiTextReadOnly} GuiTextMode; + +typedef struct + { + int x,y,w,h; + } BoundBox; + +typedef struct + { + ObjType type; + BoundBox box; + int focus; + int take_focus; + } GuiObjCommon; + +typedef struct + { + GuiObjCommon common; + const char *title; + int invert; + GfxImage *img; + } GuiObjBox; + +typedef struct + { + GuiObjCommon common; + const char *text; + int col; + int centre; + } GuiObjLabel; + +typedef struct + { + GuiObjCommon common; + const char *text; + } GuiObjButton; + +typedef struct + { + GuiObjCommon common; + char text[PATH_MAX+1]; + GuiTextMode mode; + int len; + int no_chars; + int first; + int curs; + int allow_exit; + } GuiObjText; + +typedef struct + { + GuiObjCommon common; + const char *text; + int state; + } GuiObjToggle; + +typedef struct + { + GuiObjCommon common; + int state; + const char *text; + int group; + } GuiObjRadio; + +typedef struct + { + GuiObjCommon common; + int curr; + int img_box; + int no; + char **items; + GFX_IMAGE *img; + int no_lines; + int top; + double sbar_step; + int scrollbar; + BoundBox scroll; + BoundBox bar; + int double_click; + } GuiObjImageList; + +typedef struct + { + char name[PATH_MAX+1]; + int is_dir; + off_t size; + } FileEnt; + +typedef struct + { + GuiObjCommon common; + int curr; + int no; + FileEnt *ent; + char dir[PATH_MAX+1]; + char *filter; + int no_lines; + int top; + double sbar_step; + int scrollbar; + BoundBox scroll; + BoundBox bar; + int double_click; + int path_text; + int file_text; + int name_width; + int size_width; + } GuiObjFileList; + +#define TE_CHUNK 256 + +typedef struct + { + int len; + char *p; + } TextEditLine; + +typedef struct + { + GuiObjCommon common; + TextEditLine *tline; + int top; + int x,y; + int col; + int lines; + int width; + int height; + int info; + int read_only; + } GuiObjTextEdit; + +typedef union + { + GuiObjCommon common; + GuiObjBox box; + GuiObjLabel lbl; + GuiObjButton but; + GuiObjText txt; + GuiObjToggle tog; + GuiObjRadio rad; + GuiObjImageList pck; + GuiObjFileList fle; + GuiObjTextEdit edt; + } GuiObj; + +static int focus=-1; + +static int dialog_no; +static GuiObj *dialog=NULL; + + +/* Types to define menus +*/ +typedef enum {MENU_OK, + MENU_CANCEL_ESC, + MENU_CANCEL_CLICK, + MENU_CHILD_RETURN} MenuStatus; + +typedef struct + { + MenuStatus mode; + int ret; + } MenuControl; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static void ExecProgram(char *prog_in, char *file) +{ + char prog[PATH_MAX]; + char *arg[PATH_MAX]; + int no; + int f; + + strcpy(prog,prog_in); + + no=0; + + arg[no]=strtok(prog," "); + + while (arg[no]) + arg[++no]=strtok(NULL," "); + + for(f=0;f>8)|((col&0xff0000)>>16); + pix.green=(col&0xff00)|((col&0xff00)>>8); + pix.blue=((col&0xff)<<8)|(col&0xff); + pix.flags=DoRed|DoBlue|DoGreen; + + XAllocColor(disp,DefaultColormap(disp,DefaultScreen(disp)),&pix); + + return pix.pixel; +} + + +static void DumpBox(const char *title, BoundBox b) +{ + Debug(("%s.x=%d\n",title,b.x)); + Debug(("%s.y=%d\n",title,b.y)); + Debug(("%s.w=%d\n",title,b.w)); + Debug(("%s.h=%d\n",title,b.h)); +} + + +static int MouseInBox(BoundBox *box, int no) +{ + int f; + + for(f=0;f=box[f].x)&&(mouse_x=box[f].y)&&(mouse_y=box->x)&&(mouse_xx+box->w))&& + ((mouse_y>=box->y)&&(mouse_yy+box->h))) + return TRUE; + + return FALSE; +} + + +static void Rect3D(int x,int y,int w,int h, int invert,int bevel) +{ + int f; + int mid,lo,hi; + + mid=GUI_MID; + + if (invert) + { + lo=GUI_HI; + hi=GUI_LO; + } + else + { + hi=GUI_HI; + lo=GUI_LO; + } + + GFX_frect(x,y,w,h,mid); + + for(f=0;fx,b->y,b->w,b->h,invert,bevel); +} + + +static void Tickbox(int x,int y,int w,int h, int pick) +{ + int f; + + if (pick) + GFX_frect(x,y,w,h,BLACK); + else + GFX_frect(x,y,w,h,WHITE); + + + for(f=0;fdialog[no].pck.no_lines) + dialog[no].pck.top=curr; + else + dialog[no].pck.top=0; + + dialog[no].pck.scroll.x=x+w-BEVEL-LIST_SCROLLSIZE; + dialog[no].pck.scroll.y=y+BEVEL; + dialog[no].pck.scroll.w=LIST_SCROLLSIZE; + dialog[no].pck.scroll.h=h-BEVEL*2+1; + + if (no_items>dialog[no].pck.no_lines) + { + dialog[no].pck.scrollbar=TRUE; + + dialog[no].pck.sbar_step=(double)h/(double)no_items; + dialog[no].pck.bar.x=dialog[no].pck.scroll.x; + dialog[no].pck.bar.y=dialog[no].pck.scroll.y; + dialog[no].pck.bar.w=dialog[no].pck.scroll.w; + dialog[no].pck.bar.h= + (dialog[no].pck.sbar_step*dialog[no].pck.no_lines+0.5); + } + else + { + dialog[no].pck.scrollbar=FALSE; + + dialog[no].pck.bar=dialog[no].pck.scroll; + } + + return no; +} + + +static int CreateFileList(int x, int y, int w, int h, + char *path, char *filter, + int path_text, int file_text, + int allow_double_click) +{ + int no=dialog_no++; + int charw; + + DialCommon(no,GuiFileList,x,y,w,h,TRUE); + + dialog[no].fle.ent=NULL; + strcpy(dialog[no].fle.dir,path); + dialog[no].fle.filter=filter; + dialog[no].fle.path_text=path_text; + dialog[no].fle.file_text=file_text; + dialog[no].fle.double_click=allow_double_click; + + dialog[no].fle.no_lines=h/LIST_TEXTHEIGHT; + + dialog[no].fle.scroll.x=x+w-BEVEL-LIST_SCROLLSIZE; + dialog[no].fle.scroll.y=y+BEVEL; + dialog[no].fle.scroll.w=LIST_SCROLLSIZE; + dialog[no].fle.scroll.h=h-BEVEL*2+1; + + charw=(w-BEVEL*2-LIST_SCROLLSIZE)/FW; + + dialog[no].fle.size_width=12; + dialog[no].fle.name_width=charw-dialog[no].fle.size_width; + + ResetText(path_text,path); + ResetText(file_text,""); + + chdir(path); + + return no; +} + +static int CreateTextEdit(int x, int y, int w, int h, + TextEditLine *lines, int no_lines, int info, + int read_only) +{ + static void TextEditInfo(GuiObjTextEdit *o); + int no=dialog_no++; + + DialCommon(no,GuiTextEdit,x,y,w,h,TRUE); + + dialog[no].edt.top=0; + dialog[no].edt.x=0; + dialog[no].edt.y=0; + dialog[no].edt.col=0; + + dialog[no].edt.tline=lines; + dialog[no].edt.lines=no_lines; + + dialog[no].edt.info=info; + dialog[no].edt.read_only=read_only; + + dialog[no].edt.width=w/FW; + dialog[no].edt.height=h/FH; + + TextEditInfo(&dialog[no].edt); + + return no; +} + + +/* ---------------------------------------- DIALOG CALLBACK FUNCTIONS +*/ +static CallbackReturn Callback(GuiObj *o, ObjEvent ev) +{ + static CallbackReturn BoxCallback(GuiObj *o ,ObjEvent ev); + static CallbackReturn LabelCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn ButtonCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn ToggleCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn RadioCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn ImageListCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn TextCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn FileListCallback(GuiObj *o,ObjEvent ev); + static CallbackReturn TextEditCallback(GuiObj *o,ObjEvent ev); + + switch(o->common.type) + { + case GuiBox: + return BoxCallback(o,ev); + case GuiLabel: + return LabelCallback(o,ev); + case GuiButton: + return ButtonCallback(o,ev); + case GuiToggle: + return ToggleCallback(o,ev); + case GuiRadio: + return RadioCallback(o,ev); + case GuiImageList: + return ImageListCallback(o,ev); + case GuiText: + return TextCallback(o,ev); + case GuiFileList: + return FileListCallback(o,ev); + case GuiTextEdit: + return TextEditCallback(o,ev); + default: + return GuiOk; + } +} + + +static CallbackReturn BoxCallback(GuiObj *o ,ObjEvent ev) +{ + GuiObjBox *b; + + b=&(o->box); + + switch (ev) + { + case GuiDraw: + if (!b->img) + { + Rect3DBox(&b->common.box,b->invert,BEVEL); + + if (b->title) + { + int x,y,w; + + x=b->common.box.x+BEVEL*2; + y=b->common.box.y+BEVEL; + w=b->common.box.w-BEVEL*4; + + CENTREX(x,y,w,GUI_TEXTBOLD,b->title); + + GFX_line(x,y+FH+FH/2,x+w,y+FH+FH/2,GUI_HI); + GFX_line(x,y+FH+FH/2+1,x+w,y+FH+FH/2+1,GUI_LO); + } + } + else + { + Rect3DBox(&b->common.box,b->invert,BEVEL); + + GFX_draw_image(b->img, + b->common.box.x+b->common.box.w/2-b->img->w/2, + b->common.box.y+b->common.box.h/2-b->img->h/2); + } + break; + + case GuiKeyPress: + break; + + case GuiButtonPress: + break; + + case GuiWheelUp: + break; + + case GuiWheelDown: + break; + + case GuiExit: + break; + } + return GuiOk; +} + + +static CallbackReturn LabelCallback(GuiObj *o ,ObjEvent ev) +{ + GuiObjLabel *l; + + l=&(o->lbl); + + switch (ev) + { + case GuiDraw: + GFX_frect(l->common.box.x,l->common.box.y, + l->common.box.w,l->common.box.h,GUI_MID); + + if (l->centre) + CENTREXY(l->common.box.x,l->common.box.y, + l->common.box.w,l->common.box.h,l->col,l->text); + else + CENTREY(l->common.box.x,l->common.box.y, + l->common.box.h,l->col,l->text); + break; + + case GuiKeyPress: + break; + + case GuiButtonPress: + break; + + case GuiWheelUp: + break; + + case GuiWheelDown: + break; + + case GuiExit: + break; + } + return GuiOk; +} + + +static CallbackReturn ButtonCallback(GuiObj *o ,ObjEvent ev) +{ + GuiObjButton *b; + + b=&(o->but); + + switch (ev) + { + case GuiDraw: + if (b->common.focus) + GFX_rect(b->common.box.x,b->common.box.y, + b->common.box.w,b->common.box.h,GUI_TEXTBOLD); + else + GFX_rect(b->common.box.x,b->common.box.y, + b->common.box.w,b->common.box.h,GUI_MID); + + Rect3D(b->common.box.x+1,b->common.box.y+1, + b->common.box.w-2,b->common.box.h-2, + FALSE,BEVEL); + + CENTREXY(b->common.box.x,b->common.box.y, + b->common.box.w,b->common.box.h,GUI_TEXTBOLD,b->text); + break; + + case GuiKeyPress: + if (key!=GFX_ENTER) + break; + + /* CAUTION: Case above runs into this + */ + case GuiButtonPress: + Rect3D(b->common.box.x+1,b->common.box.y+1, + b->common.box.w-2,b->common.box.h-2, + TRUE,BEVEL); + + CENTREXY(b->common.box.x,b->common.box.y, + b->common.box.w,b->common.box.h,GUI_TEXTBOLD,b->text); + + return GuiDone; + break; + + case GuiWheelUp: + break; + + case GuiWheelDown: + break; + + case GuiExit: + break; + } + + return GuiOk; +} + + +static CallbackReturn ToggleCallback(GuiObj *op ,ObjEvent ev) +{ + GuiObjToggle *o; + int x,y,w,h; + + o=&(op->tog); + + x=o->common.box.x; + y=o->common.box.y; + w=o->common.box.h; + h=o->common.box.h; + + switch (ev) + { + case GuiDraw: + if (o->common.focus) + GFX_rect(x,y,w,h,GUI_TEXTBOLD); + else + GFX_rect(x,y,w,h,GUI_MID); + + CENTREY(o->common.box.x+o->common.box.h+FW,o->common.box.y, + o->common.box.h,GUI_TEXT,o->text); + + Tickbox(x+1,y+1,w-2,h-2,o->state); + + break; + + case GuiKeyPress: + if (key!=GFX_ENTER && ascii!=' ') + break; + + /* CAUTION: Case above runs into this + */ + case GuiButtonPress: + o->state=!o->state; + return GuiRedraw; + break; + + case GuiWheelUp: + break; + + case GuiWheelDown: + break; + + case GuiExit: + break; + } + + return GuiOk; +} + + +static CallbackReturn RadioCallback(GuiObj *op ,ObjEvent ev) +{ + GuiObjRadio *o; + int x,y,w,h; + + o=&(op->rad); + + x=o->common.box.x; + y=o->common.box.y; + w=o->common.box.h; + h=o->common.box.h; + + switch (ev) + { + case GuiDraw: + if (o->common.focus) + GFX_rect(x,y,w,h,GUI_TEXTBOLD); + else + GFX_rect(x,y,w,h,GUI_MID); + + CENTREY(o->common.box.x+o->common.box.h+FW,o->common.box.y, + o->common.box.h,GUI_TEXT,o->text); + + Tickbox(x+1,y+1,w-2,h-2,o->state); + + break; + + case GuiKeyPress: + if (key!=GFX_ENTER && ascii!=' ') + break; + + /* CAUTION: Case above runs into this + */ + case GuiButtonPress: + if (!o->state) + { + int f; + + for(f=0;fgroup) + dialog[f].rad.state=FALSE; + + o->state=TRUE; + + return GuiRedraw; + } + break; + + case GuiWheelUp: + break; + + case GuiWheelDown: + break; + + case GuiExit: + break; + } + + return GuiOk; +} + + +static CallbackReturn HandleImageListPage(GuiObjImageList *o, int keycode) +{ + switch(keycode) + { + case GFX_UP: + case GFX_PGUP: + if (o->curr>0) + { + o->curr=MAX(0,o->curr-(key==GFX_UP ? 1:o->no_lines)); + + if (o->currtop) + o->top=o->curr; + + if ((o->top+o->no_lines)<=o->curr) + o->top=o->curr-o->no_lines+1; + + return GuiRedraw; + } + break; + + case GFX_DOWN: + case GFX_PGDN: + if (o->curr<(o->no-1)) + { + o->curr=MIN(o->no-1,o->curr+ + (key==GFX_DOWN ? 1:o->no_lines)); + + if (o->currtop) + o->top=o->curr; + + if ((o->top+o->no_lines)<=o->curr) + o->top=o->curr-o->no_lines+1; + + return GuiRedraw; + } + break; + + default: + break; + } + + return GuiOk; +} + + +static CallbackReturn ImageListCallback(GuiObj *op ,ObjEvent ev) +{ + static time_t last=0; + GuiObjImageList *o; + int x,y,w,h; + int sx,sy,sw,sh; + int f; + + o=&(op->pck); + + x=o->common.box.x+BEVEL; + y=o->common.box.y+BEVEL; + w=o->common.box.w-BEVEL*2; + h=o->common.box.h-BEVEL*2; + + sx=o->scroll.x; + sy=o->scroll.y; + sw=o->scroll.w; + sh=o->scroll.h; + + switch (ev) + { + case GuiDraw: + if (o->common.focus) + GFX_rect(x-BEVEL-1,y-BEVEL-1, + w+BEVEL*2+2,h+BEVEL*2+2,GUI_TEXTBOLD); + else + GFX_rect(x-BEVEL-1,y-BEVEL-1, + w+BEVEL*2+2,h+BEVEL*2+2,GUI_MID); + + Rect3DBox(&o->common.box,TRUE,BEVEL); + + GFX_frect(x,y,w,h,WHITE); + + GFX_frect(sx,sy,sw,sh,GUI_MID); + + o->bar.y=sy+o->sbar_step*o->top; + Rect3DBox(&o->bar,FALSE,SMALL_BEVEL); + + for(f=0;fno_lines;f++) + { + int col; + int p=f+o->top; + int tx=x; + int ty=y+(f*LIST_TEXTHEIGHT); + int tw=w-sw; + int th=LIST_TEXTHEIGHT; + + if (p==o->curr) + { + col=WHITE; + GFX_frect(tx,ty,tw,th,BLACK); + } + else + col=BLACK; + + if (pno) + CENTREY(tx,ty,th,col,o->items[p]); + } + + if (o->img) + { + dialog[o->img_box].box.img=o->img[o->curr]; + Callback(dialog+o->img_box,GuiDraw); + } + + break; + + case GuiKeyPress: + switch(key) + { + case GFX_UP: + case GFX_PGUP: + case GFX_DOWN: + case GFX_PGDN: + return HandleImageListPage(o,key); + break; + + case GFX_ENTER: + return GuiDone; + break; + + default: + break; + } + + break; + + case GuiButtonPress: + if (IsInBox(&o->scroll)) + { + if (!o->scrollbar) + return GuiOk; + + if (IsInBox(&o->bar)) + { + int orig; + int y; + double dtop; + + orig=mouse_y; + dtop=o->top; + + while (GFX_mouse(NULL,&y)==GFX_BUTLEFT) + { + int diff; + + diff=y-orig; + + dtop+=diff/o->sbar_step; + + o->top=(int)dtop; + + o->top=MAX(0,o->top); + o->top=MIN(o->no-o->no_lines,o->top); + + Callback(op,GuiDraw); + GFX_redraw(); + + orig=y; + } + } + else + { + if (mouse_ybar.y) + o->top=MAX(0,o->top-o->no_lines); + else + o->top=MIN(o->no-1-o->no_lines,o->top+o->no_lines); + + return GuiRedraw; + } + } + else + { + int my; + int pick; + + my=mouse_y-y; + + pick=o->top+my/LIST_TEXTHEIGHT; + + if (pickno) + { + if (pick==o->curr && (time(NULL)-last)<2 && + o->double_click) + return GuiDone; + + last=time(NULL); + + o->curr=pick; + return GuiRedraw; + } + } + break; + + case GuiWheelUp: + return HandleImageListPage(o,GFX_PGUP); + break; + + case GuiWheelDown: + return HandleImageListPage(o,GFX_PGDN); + break; + + case GuiExit: + break; + } + + return GuiOk; +} + + +static CallbackReturn TextCallback(GuiObj *op ,ObjEvent ev) +{ + int x,y,w,h; + GuiObjText *o; + int f; + int ok; + + o=&(op->txt); + + x=o->common.box.x+BEVEL; + y=o->common.box.y+BEVEL; + w=o->common.box.w-BEVEL*2; + h=o->common.box.h-BEVEL*2; + + switch (ev) + { + case GuiDraw: + if (o->common.focus) + GFX_rect(x-BEVEL-1,y-BEVEL-1, + w+BEVEL*2+2,h+BEVEL*2+2,GUI_TEXTBOLD); + else + GFX_rect(x-BEVEL-1,y-BEVEL-1, + w+BEVEL*2+2,h+BEVEL*2+2,GUI_MID); + + Rect3DBox(&o->common.box,TRUE,BEVEL); + + if (o->mode!=GuiTextReadOnly) + GFX_frect(x,y,w,h,WHITE); + + for(f=0;fno_chars;f++) + { + int col; + int pos; + int tx; + + tx=x+f*FW; + pos=o->first+f; + + if (o->common.focus) + { + if (pos==o->curs) + { + GFX_frect(tx,y,FW,FH,BLACK); + col=WHITE; + } + else + col=BLACK; + } + else + col=BLACK; + + if (poslen) + GFX_print(tx,y,col,"%c",o->text[pos]); + + tx+=FW; + } + + break; + + case GuiKeyPress: + switch(key) + { + case GFX_LEFT: + o->curs--; + o->curs=MAX(0,o->curs); + o->curs=MIN(o->len,o->curs); + + if (o->cursfirst) + o->first--; + + return GuiRedraw; + break; + + case GFX_RIGHT: + o->curs++; + o->curs=MIN(o->len,o->curs); + + if (o->curs>(o->first+o->no_chars-1)) + o->first++; + + return GuiRedraw; + break; + + case GFX_ENTER: + if (o->allow_exit) + return GuiDone; + break; + + case GFX_BACKSPACE: + case GFX_DELETE: + if (o->curs) + { + memmove(o->text+o->curs-1,o->text+o->curs, + o->len-o->curs+1); + + if (--o->cursfirst) + o->first--; + + o->len=strlen(o->text); + + return GuiRedraw; + } + break; + + case GFX_END: + o->curs=o->len; + o->first=MAX(0,o->len-o->no_chars-1); + return GuiRedraw; + break; + + case GFX_HOME: + o->curs=0; + o->first=0; + return GuiRedraw; + break; + + default: + switch(o->mode) + { + case GuiTextString: + ok=isprint(ascii); + break; + case GuiTextInteger: + if (strchr("-+0123456789",ascii)) + ok=TRUE; + else + ok=FALSE; + break; + case GuiTextDouble: + if (strchr(".-+0123456789",ascii)) + ok=TRUE; + else + ok=FALSE; + break; + case GuiTextReadOnly: + ok=FALSE; + break; + default: + ok=FALSE; + break; + } + + if (ok && o->lencurs==o->len) + { + o->text[o->curs++]=ascii; + o->text[o->curs]=0; + } + else + { + memmove(o->text+o->curs+1,o->text+o->curs, + o->len-o->curs+1); + o->text[o->curs++]=ascii; + } + + o->len=strlen(o->text); + + if (o->curs>(o->first+o->no_chars-1)) + o->first++; + + return GuiRedraw; + } + + break; + } + + break; + + case GuiButtonPress: + break; + + case GuiWheelUp: + break; + + case GuiWheelDown: + break; + + case GuiExit: + break; + } + + return GuiOk; +} + + +static CallbackReturn HandleFileListPage(GuiObjFileList *o, int keycode) +{ + switch(keycode) + { + case GFX_UP: + case GFX_PGUP: + if (o->curr>0) + { + o->curr=MAX(0,o->curr-(key==GFX_UP ? 1:o->no_lines)); + + if (o->currtop) + o->top=o->curr; + + if ((o->top+o->no_lines)<=o->curr) + o->top=o->curr-o->no_lines+1; + } + break; + + case GFX_DOWN: + case GFX_PGDN: + if (o->curr<(o->no-1)) + { + o->curr=MIN(o->no-1,o->curr+ + (key==GFX_DOWN ? 1:o->no_lines)); + + if (o->currtop) + o->top=o->curr; + + if ((o->top+o->no_lines)<=o->curr) + o->top=o->curr-o->no_lines+1; + } + break; + + default: + break; + } + + if (o->ent[o->curr].is_dir) + ResetText(o->file_text,""); + else + ResetText(o->file_text,o->ent[o->curr].name); + + return GuiRedraw; +} + + +static int SortFiles(const void *a, const void *b) +{ + const FileEnt *fa,*fb; + + fa=a; + fb=b; + + if (fa->is_dir && !fb->is_dir) + return -1; + else if (!fa->is_dir && fb->is_dir) + return 1; + else + return strcmp(fa->name,fb->name); +} + + +static int FilesMatch(const char *file, const char *filter) +{ + if (!filter) + return TRUE; + + if (strlen(file)ent) + Release(o->ent); + + o->ent=NULL; + o->no=0; + o->curr=0; + o->top=0; + + getcwd(o->dir,PATH_MAX); + + if (o->dir[l=(strlen(o->dir)-1)]!='/') + { + o->dir[++l]='/'; + o->dir[++l]=0; + } + + if ((d=opendir(o->dir))) + { + struct dirent *ent; + struct stat sbuf; + + while ((ent=readdir(d))) + { + if (!stat(ent->d_name,&sbuf)) + if (S_ISDIR(sbuf.st_mode) || FilesMatch(ent->d_name,o->filter)) + { + o->no++; + o->ent=ReGrab(o->ent,sizeof(FileEnt)*o->no); + + strcpy(o->ent[o->no-1].name,ent->d_name); + o->ent[o->no-1].is_dir=S_ISDIR(sbuf.st_mode); + o->ent[o->no-1].size=sbuf.st_size; + } + } + + closedir(d); + } + + ResetText(o->file_text,""); + ResetText(o->path_text,o->dir); + + qsort(o->ent,o->no,sizeof(FileEnt),SortFiles); + + if (o->no>o->no_lines) + { + o->scrollbar=TRUE; + + o->sbar_step=(double)o->common.box.h/(double)o->no; + o->bar.x=o->scroll.x; + o->bar.y=o->scroll.y; + o->bar.w=o->scroll.w; + o->bar.h=(o->sbar_step*o->no_lines+0.5); + } + else + { + o->scrollbar=FALSE; + o->bar=o->scroll; + } +} + + +static CallbackReturn FileListCallback(GuiObj *op ,ObjEvent ev) +{ + static time_t last=0; + static char buff[PATH_MAX+10]; + GuiObjFileList *o; + int x,y,w,h; + int sx,sy,sw,sh; + int f; + + o=&(op->fle); + + if (!o->ent) + GetFileList(o); + + x=o->common.box.x+BEVEL; + y=o->common.box.y+BEVEL; + w=o->common.box.w-BEVEL*2; + h=o->common.box.h-BEVEL*2; + + sx=o->scroll.x; + sy=o->scroll.y; + sw=o->scroll.w; + sh=o->scroll.h; + + switch (ev) + { + case GuiDraw: + if (o->common.focus) + GFX_rect(x-BEVEL-1,y-BEVEL-1, + w+BEVEL*2+2,h+BEVEL*2+2,GUI_TEXTBOLD); + else + GFX_rect(x-BEVEL-1,y-BEVEL-1, + w+BEVEL*2+2,h+BEVEL*2+2,GUI_MID); + + Rect3DBox(&o->common.box,TRUE,BEVEL); + + GFX_frect(x,y,w,h,WHITE); + + GFX_frect(sx,sy,sw,sh,GUI_MID); + + o->bar.y=sy+o->sbar_step*o->top; + Rect3DBox(&o->bar,FALSE,SMALL_BEVEL); + + for(f=0;fno_lines;f++) + { + int col; + int p=f+o->top; + int tx=x; + int ty=y+(f*LIST_TEXTHEIGHT); + int tw=w-sw; + int th=LIST_TEXTHEIGHT; + + if (p==o->curr) + { + col=WHITE; + GFX_frect(tx,ty,tw,th,BLACK); + } + else + col=BLACK; + + if (pno) + { + if (o->ent[p].is_dir) + sprintf(buff,"%-*.*s%*.*s", + o->name_width, + o->name_width, + o->ent[p].name, + o->size_width, + o->size_width, + ""); + else + sprintf(buff,"%-*.*s%*ld", + o->name_width, + o->name_width, + o->ent[p].name, + o->size_width, + o->ent[p].size); + + CENTREY(tx,ty,th,col,buff); + } + } + + break; + + case GuiKeyPress: + switch(key) + { + case GFX_UP: + case GFX_PGUP: + case GFX_DOWN: + case GFX_PGDN: + return HandleFileListPage(o,key); + break; + + case GFX_ENTER: + if (!o->ent[o->curr].is_dir) + return GuiDone; + break; + + default: + break; + } + + break; + + case GuiButtonPress: + if (IsInBox(&o->scroll)) + { + if (!o->scrollbar) + return GuiOk; + + if (IsInBox(&o->bar)) + { + int orig; + int y; + double dtop; + + orig=mouse_y; + dtop=o->top; + + while (GFX_mouse(NULL,&y)==GFX_BUTLEFT) + { + int diff; + + diff=y-orig; + + dtop+=diff/o->sbar_step; + + o->top=(int)dtop; + + o->top=MAX(0,o->top); + o->top=MIN(o->no-o->no_lines,o->top); + + Callback(op,GuiDraw); + GFX_redraw(); + + orig=y; + } + } + else + { + if (mouse_ybar.y) + o->top=MAX(0,o->top-o->no_lines); + else + o->top=MIN(o->no-1-o->no_lines,o->top+o->no_lines); + + return GuiRedraw; + } + } + else + { + int my; + int pick; + + my=mouse_y-y; + + pick=o->top+my/LIST_TEXTHEIGHT; + + if (pickno) + { + if (pick==o->curr && (time(NULL)-last)<2 && o->double_click) + { + if (o->ent[o->curr].is_dir) + { + chdir(o->ent[o->curr].name); + GetFileList(o); + last=0; + return GuiRedraw; + } + else + return GuiDone; + } + + last=time(NULL); + + o->curr=pick; + + if (!o->ent[o->curr].is_dir) + ResetText(o->file_text,o->ent[o->curr].name); + + return GuiRedraw; + } + } + break; + + case GuiWheelUp: + return HandleFileListPage(o,GFX_PGUP); + break; + + case GuiWheelDown: + return HandleFileListPage(o,GFX_PGDN); + break; + + case GuiExit: + if (o->ent) + Release(o->ent); + break; + } + + return GuiOk; +} + + +static void TextEditInfo(GuiObjTextEdit *o) +{ + char b1[128]; + char b2[128]; + char buff[128]; + + sprintf(b1,"Cursor: %d,%d",o->x+1,o->y+1); + sprintf(b2,"Lines: %d",o->lines); + sprintf(buff,"%-20.20s %s",b1,b2); + + ResetText(o->info,buff); +} + + +static CallbackReturn TextEditCallback(GuiObj *op ,ObjEvent ev) +{ + char out; + char *tmp; + GuiObjTextEdit *te; + TextEditLine *p; + int bx,by,bw,bh; + int x,y,sx,sy; + int f; + int mod; + + te=&op->edt; + p=te->tline; + + bx=te->common.box.x; + by=te->common.box.y; + bw=te->common.box.w; + bh=te->common.box.h; + + switch(ev) + { + case GuiDraw: + if (te->common.focus) + GFX_rect(bx-2,by-2,bw+4,bh+4,GUI_TEXTBOLD); + else + GFX_rect(bx-2,by-2,bw+4,bh+4,GUI_MID); + + GFX_frect(bx,by,bw,bh,WHITE); + + y=te->top; + + for(sy=0;syheight;sy++,y++) + if (ylines) + { + x=te->col; + + for(sx=0;sxwidth;sx++,x++) + { + if (xx && y==te->y) + { + GFX_frect(bx+(sx*FW),by+(sy*FH),FW,FH,BLACK); + GFX_print(bx+(sx*FW),by+(sy*FH),WHITE,"%c",out); + } + else + GFX_print(bx+(sx*FW),by+(sy*FH),BLACK,"%c",out); + } + } + + break; + + case GuiButtonPress: + te->y=(mouse_y-by)/FH; + te->x=te->col+(mouse_x-bx)/FW; + + if (te->y>=te->lines) + te->y=te->lines-1; + + if (te->x>p[te->y].len) + te->x=p[te->y].len; + + TextEditInfo(te); + return GuiRedraw; + break; + + case GuiKeyPress: + switch(key) + { + case GFX_HOME: + te->x=0; + te->col=0; + break; + + case GFX_END: + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + break; + + case GFX_LEFT: + mod=ctrl||shift; + + if (mod) + while((te->x)&&(isspace(p[te->y].p[te->x]))) + { + if (te->x) + te->x--; + else + mod=FALSE; + + if (te->xcol) + te->col--; + } + + do { + if (te->x) + te->x--; + else + mod=FALSE; + + if (te->xcol) + te->col--; + + if (isspace(p[te->y].p[te->x])) + mod=FALSE; + + } while(mod); + + break; + + case GFX_RIGHT: + mod=ctrl||shift; + + if (mod) + while((te->xy].len)&& + (isspace(p[te->y].p[te->x]))) + { + te->x++; + + if ((te->x-te->width)>te->col) + te->col++; + } + + do { + if (te->xy].len) + { + te->x++; + + if ((te->x-te->width)>te->col) + te->col++; + } + else + mod=FALSE; + + if (isspace(p[te->y].p[te->x])) + mod=FALSE; + + } while(mod); + + break; + + case GFX_UP: + if (te->y) + { + te->y--; + + if (te->ytop) + te->top--; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + } + } + break; + + case GFX_PGUP: + if (te->y) + { + te->y-=te->height; + + if (te->y<0) + te->y=0; + + if (te->ytop) + te->top=te->y; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + } + } + break; + + case GFX_DOWN: + if (p[te->y+1].p) + { + te->y++; + + if (te->y>=te->top+te->height) + te->top=te->y-te->height+1; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + } + } + break; + + case GFX_PGDN: + te->y+=te->height; + + if (te->y>=te->lines) + te->y=te->lines-1; + + if (te->y>=te->top+te->height) + te->top=te->y-te->height+1; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + } + + break; + + case GFX_DELETE: + if (te->read_only) + break; + + if (p[te->y].p[te->x]) + { + for(f=te->x;fy].len;f++) + p[te->y].p[f]=p[te->y].p[f+1]; + + p[te->y].len--; + p[te->y].p[p[te->y].len]=0; + } + else if (p[te->y+1].p) + { + p[te->y].len+=p[te->y+1].len; + + p[te->y].p=ReGrab(p[te->y].p, + (p[te->y].len/TE_CHUNK+1)*TE_CHUNK+1); + + strcat(p[te->y].p,p[te->y+1].p); + + Release(p[te->y+1].p); + + for(f=te->y+1;flines;f++) + p[f]=p[f+1]; + + te->lines--; + + p=te->tline=ReGrab(te->tline, + sizeof(TextEditLine)*(te->lines+1)); + p[te->lines].p=NULL; + } + break; + + case GFX_BACKSPACE: + if (te->read_only) + break; + + if (te->x) + { + for(f=te->x;fy].len;f++) + p[te->y].p[f-1]=p[te->y].p[f]; + + p[te->y].len--; + te->x--; + p[te->y].p[p[te->y].len]=0; + + if (te->xcol) + te->col--; + } + else if (te->y) + { + te->x=p[te->y-1].len; + te->col=MAX(0,te->x-te->width); + + p[te->y-1].len+=p[te->y].len; + + p[te->y-1].p=ReGrab(p[te->y-1].p, + (p[te->y-1].len/TE_CHUNK+1)*TE_CHUNK+1); + + strcat(p[te->y-1].p,p[te->y].p); + + Release(p[te->y].p); + + for(f=te->y;flines;f++) + p[f]=p[f+1]; + + if (--te->ytop) + te->top--; + + te->lines--; + p=te->tline=ReGrab(te->tline, + sizeof(TextEditLine)*(te->lines+1)); + p[te->lines].p=NULL; + } + break; + + case GFX_ENTER: + if (te->read_only) + break; + + te->lines++; + p=te->tline=ReGrab(te->tline, + sizeof(TextEditLine)*(te->lines+1)); + + for(f=te->lines;f>te->y;f--) + p[f]=p[f-1]; + + tmp=Strdup(p[te->y].p+te->x); + tmp=ReGrab(tmp,(strlen(tmp)/TE_CHUNK+1)*TE_CHUNK+1); + + p[te->y].p[te->x]=0; + p[te->y].len=strlen(p[te->y].p); + + te->y++; + te->x=0; + te->col=0; + + p[te->y].p=tmp; + p[te->y].len=strlen(tmp); + + if (te->y>=te->top+te->height) + te->top=te->y-te->height+1; + + break; + + default: + if (ascii && !te->read_only) + { + p[te->y].len++; + + if (!(p[te->y].len%TE_CHUNK)) + p[te->y].p=ReGrab(p[te->y].p, + (p[te->y].len/TE_CHUNK+1)* + TE_CHUNK+1); + + for(f=p[te->y].len;f>te->x;f--) + p[te->y].p[f]=p[te->y].p[f-1]; + + p[te->y].p[te->x]=ascii; + + te->x++; + + if ((te->x-te->width)>te->col) + te->col++; + } + break; + } + + TextEditInfo(te); + return GuiRedraw; + break; + + case GuiWheelUp: + if (te->y) + { + te->y-=te->height; + + if (te->y<0) + te->y=0; + + if (te->ytop) + te->top=te->y; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + } + } + + TextEditInfo(te); + return GuiRedraw; + break; + + case GuiWheelDown: + te->y+=te->height; + + if (te->y>=te->lines) + te->y=te->lines-1; + + if (te->y>=te->top+te->height) + te->top=te->y-te->height+1; + + if (te->x>p[te->y].len) + { + te->x=p[te->y].len; + te->col=MAX(0,te->x-te->width); + } + + TextEditInfo(te); + return GuiRedraw; + break; + + case GuiExit: + break; + } + + return GuiOk; +} + + +/* ---------------------------------------- DIALOG MAIN LOOP +*/ +static int DoDial(void) +{ + Pixmap saved; + int done=FALSE; + int f; + + saved=VIDOOM_GFX_SAVE_UNDER(dialog[0].common.box.x, + dialog[0].common.box.y, + dialog[0].common.box.w, + dialog[0].common.box.h); + + for(f=0;(fx,b->y,b->w,b->h,GUI_MID); + + GFX_print(b->x+SMALL_BEVEL+2,b->y+b->h/2-FH/2,GUI_TEXT,txt); + + if (child) + { + h=b->h-(SMALL_BEVEL*2)-2; + hy=b->y+SMALL_BEVEL+1; + + GFX_line(b->x+b->w-SMALL_BEVEL-2-h/2,hy, + b->x+b->w-SMALL_BEVEL-2,hy+h/2,GUI_HI); + + GFX_line(b->x+b->w-SMALL_BEVEL-2-h/2,hy, + b->x+b->w-SMALL_BEVEL-2-h/2,hy+h,GUI_HI); + + GFX_line(b->x+b->w-SMALL_BEVEL-2-h/2,hy+h, + b->x+b->w-SMALL_BEVEL-2,hy+h/2,GUI_LO); + } +} + + +static MenuControl do_GUI_menu(char *title, int x, int y, + PLAT_MENU menu[],int defval,int is_child) +{ + Pixmap under; + MenuControl ret; + MenuControl cr; + int no; + int f; + int cur; + int last_cur; + int cx; + int by; + int done; + int quit; + int child_done; + BoundBox *box; + BoundBox menu_box; + int do_child; + + /* Calc dimensions + */ + no=0; + menu_box.x=x; + menu_box.y=y; + menu_box.w=LENGTH(title); + + while(menu[no].text) + { + int l; + + l=LENGTH(menu[no].text)+(menu[no].child ? 12 : 0); + menu_box.w=MAX(menu_box.w,l); + no++; + } + + menu_box.h=BEVEL*2+FH+4; + menu_box.h+=(FH+SMALL_BEVEL*2+2)*no; + menu_box.h+=2; + menu_box.w+=BEVEL+SMALL_BEVEL+4; + + box=Grab(sizeof(*box)*no); + + if ((menu_box.x+menu_box.w)>SCRW) + menu_box.x=SCRW-menu_box.w; + + if ((menu_box.y+menu_box.h)>SCRH) + menu_box.y=SCRH-menu_box.h; + + if ((menu_box.x<0)||(menu_box.y<0)) + { + fprintf(stderr, + "Cannot display menu:%s\nMenu is bigger than display!\n",title); + + ret.mode=MENU_CHILD_RETURN; + ret.ret=defval; + + return ret; + } + + under=VIDOOM_GFX_SAVE_UNDER(menu_box.x,menu_box.y,menu_box.w,menu_box.h); + + cx=menu_box.x+menu_box.w/2; + + /* Draw menu border and title + */ + Rect3DBox(&menu_box,FALSE,BEVEL); + CENTRE(cx,menu_box.y+BEVEL*2,GUI_TEXTBOLD,title); + + /* Draw menu items + */ + cur=0; + last_cur=0; + + by=menu_box.y+BEVEL*2+FH+4; + + for(f=0;fw); + max_imgh=MAX(max_imgh,i->h); + } + + no++; + } + + optset=Grab(sizeof(char *)*no); + imgset=Grab(sizeof(GFX_IMAGE)*no); + + for(f=0;f(SCRW-FW*5)) + width-=FW; + + while (height>(SCRH-FH*5)) + height-=FH; + + x=(SCRW-width)/2; + y=(SCRH-height)/2; + + info=CreateText(x,y-FH*2,width,FH*1.5,"",GuiTextReadOnly,FALSE); + edit=CreateTextEdit(x,y,width,height,line,no,info,TRUE); + + ok=CreateButton(x+10,y+height+FH*2,width-20,FH*2,"OK"); + + ResizeBox(box); + + DoDial(); + + for(f=0;f(SCRW-FW*5)) + width-=FW; + + while (height>(SCRH-FH*5)) + height-=FH; + + x=(SCRW-width)/2; + y=(SCRH-height)/2; + + info=CreateText(x,y-FH*2,width,FH*1.5,"",GuiTextReadOnly,FALSE); + edit=CreateTextEdit(x,y,width,height,line,no,info,FALSE); + + ok=CreateButton(x+10,y+height+FH*2,width/2-20,FH*2,"OK"); + cancel=CreateButton(SCRW/2+10,y+height+FH*2,width/2-20,FH*2,"Cancel"); + + ResizeBox(box); + + press=DoDial(); + + if (press==ok) + { + int tot; + + tot=0; + + for(f=0;f +#include + +#define MAX_CMDSIZE 8192 + +static void Write(int fd, char *p) +{ + /* Should really do a loopy proper one... + */ + write(fd,p,strlen(p)); +} + + +int RunCommand(char *argv[], char *path) +{ + FILE *fp; + int output; + int f; + char buff[MAX_CMDSIZE]; + + f=0; + buff[0]=0; + + while(argv[f]) + { + strcat(buff,argv[f++]); + strcat(buff," "); + } + + strcpy(path,"vidoomXXXXXX"); + + if ((output=mkstemp(path))==-1) + return FALSE; + + if (!(fp=popen(buff,"r"))) + return FALSE; + + fgets(buff,sizeof buff,fp); + + while(!feof(fp)) + { + Write(output,buff); + fgets(buff,sizeof buff,fp); + } + + /* Assume that non-zero is error + */ + return pclose(fp)==0; +} + + +/* END OF FILE */ diff --git a/linux/vstring.c b/linux/vstring.c new file mode 100644 index 0000000..947ca99 --- /dev/null +++ b/linux/vstring.c @@ -0,0 +1,48 @@ +/* + + 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 + + ------------------------------------------------------------------------- + + Provides portable versions of necessary string routines + +*/ +static const char rcs_id[]="$Id$"; + +#include + + +int StrCaseCmp(char *a, char *b) +{ + if (!a || !b) + return 0; + + return strcasecmp(a,b); +} + +int StrNCaseCmp(char *a, char *b, int n) +{ + if (!a || !b) + return 0; + + return strncasecmp(a,b,n); +} + + +/* END OF FILE */ -- cgit v1.2.3