1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20// vid_x.c -- general x video driver
21
22#define _BSD
23
24typedef unsigned short PIXEL;
25
26#include <ctype.h>
27#include <sys/time.h>
28#include <sys/types.h>
29#include <unistd.h>
30#include <signal.h>
31#include <stdlib.h>
32#include <stdio.h>
33#include <string.h>
34#include <sys/ipc.h>
35#include <sys/shm.h>
36#include <X11/Xlib.h>
37#include <X11/Xutil.h>
38#include <X11/Xatom.h>
39#include <X11/keysym.h>
40#include <X11/extensions/XShm.h>
41
42#include "quakedef.h"
43#include "d_local.h"
44
45cvar_t		_windowed_mouse = {"_windowed_mouse","0", true};
46cvar_t		m_filter = {"m_filter","0", true};
47float old_windowed_mouse;
48
49// not used
50int		VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar;
51byte	*VGA_pagebase;
52
53
54qboolean        mouse_avail;
55int             mouse_buttons=3;
56int             mouse_oldbuttonstate;
57int             mouse_buttonstate;
58float   mouse_x, mouse_y;
59float   old_mouse_x, old_mouse_y;
60int p_mouse_x;
61int p_mouse_y;
62int ignorenext;
63int bits_per_pixel;
64
65typedef struct
66{
67	int input;
68	int output;
69} keymap_t;
70
71viddef_t vid; // global video state
72unsigned short d_8to16table[256];
73
74int		num_shades=32;
75
76int	d_con_indirect = 0;
77
78int		vid_buffersize;
79
80static qboolean			doShm;
81static Display			*x_disp;
82static Colormap			x_cmap;
83static Window			x_win;
84static GC				x_gc;
85static Visual			*x_vis;
86static XVisualInfo		*x_visinfo;
87//static XImage			*x_image;
88
89static int				x_shmeventtype;
90//static XShmSegmentInfo	x_shminfo;
91
92static qboolean			oktodraw = false;
93
94int XShmQueryExtension(Display *);
95int XShmGetEventBase(Display *);
96
97int current_framebuffer;
98static XImage			*x_framebuffer[2] = { 0, 0 };
99static XShmSegmentInfo	x_shminfo[2];
100
101static int verbose=0;
102
103static byte current_palette[768];
104
105static long X11_highhunkmark;
106static long X11_buffersize;
107
108int vid_surfcachesize;
109void *vid_surfcache;
110
111void (*vid_menudrawfn)(void);
112void (*vid_menukeyfn)(int key);
113void VID_MenuKey (int key);
114
115static PIXEL st2d_8to16table[256];
116static int shiftmask_fl=0;
117static long r_shift,g_shift,b_shift;
118static unsigned long r_mask,g_mask,b_mask;
119
120void shiftmask_init()
121{
122    unsigned int x;
123    r_mask=x_vis->red_mask;
124    g_mask=x_vis->green_mask;
125    b_mask=x_vis->blue_mask;
126    for(r_shift=-8,x=1;x<r_mask;x=x<<1)r_shift++;
127    for(g_shift=-8,x=1;x<g_mask;x=x<<1)g_shift++;
128    for(b_shift=-8,x=1;x<b_mask;x=x<<1)b_shift++;
129    shiftmask_fl=1;
130}
131
132PIXEL xlib_rgb(int r,int g,int b)
133{
134    PIXEL p;
135    if(shiftmask_fl==0) shiftmask_init();
136    p=0;
137
138    if(r_shift>0) {
139        p=(r<<(r_shift))&r_mask;
140    } else if(r_shift<0) {
141        p=(r>>(-r_shift))&r_mask;
142    } else p|=(r&r_mask);
143
144    if(g_shift>0) {
145        p|=(g<<(g_shift))&g_mask;
146    } else if(g_shift<0) {
147        p|=(g>>(-g_shift))&g_mask;
148    } else p|=(g&g_mask);
149
150    if(b_shift>0) {
151        p|=(b<<(b_shift))&b_mask;
152    } else if(b_shift<0) {
153        p|=(b>>(-b_shift))&b_mask;
154    } else p|=(b&b_mask);
155
156    return p;
157}
158
159void st2_fixup( XImage *framebuf, int x, int y, int width, int height)
160{
161	int xi,yi;
162	unsigned char *src;
163	PIXEL *dest;
164
165	if( (x<0)||(y<0) )return;
166
167	for (yi = y; yi < (y+height); yi++) {
168		src = &framebuf->data [yi * framebuf->bytes_per_line];
169		dest = (PIXEL*)src;
170		for(xi = (x+width-1); xi >= x; xi--) {
171			dest[xi] = st2d_8to16table[src[xi]];
172		}
173	}
174}
175
176
177// ========================================================================
178// Tragic death handler
179// ========================================================================
180
181void TragicDeath(int signal_num)
182{
183	XAutoRepeatOn(x_disp);
184	XCloseDisplay(x_disp);
185	Sys_Error("This death brought to you by the number %d\n", signal_num);
186}
187
188// ========================================================================
189// makes a null cursor
190// ========================================================================
191
192static Cursor CreateNullCursor(Display *display, Window root)
193{
194    Pixmap cursormask;
195    XGCValues xgc;
196    GC gc;
197    XColor dummycolour;
198    Cursor cursor;
199
200    cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
201    xgc.function = GXclear;
202    gc =  XCreateGC(display, cursormask, GCFunction, &xgc);
203    XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
204    dummycolour.pixel = 0;
205    dummycolour.red = 0;
206    dummycolour.flags = 04;
207    cursor = XCreatePixmapCursor(display, cursormask, cursormask,
208          &dummycolour,&dummycolour, 0,0);
209    XFreePixmap(display,cursormask);
210    XFreeGC(display,gc);
211    return cursor;
212}
213
214void ResetFrameBuffer(void)
215{
216	int mem;
217	int pwidth;
218
219	if (x_framebuffer[0])
220	{
221		free(x_framebuffer[0]->data);
222		free(x_framebuffer[0]);
223	}
224
225	if (d_pzbuffer)
226	{
227		D_FlushCaches ();
228		Hunk_FreeToHighMark (X11_highhunkmark);
229		d_pzbuffer = NULL;
230	}
231	X11_highhunkmark = Hunk_HighMark ();
232
233// alloc an extra line in case we want to wrap, and allocate the z-buffer
234	X11_buffersize = vid.width * vid.height * sizeof (*d_pzbuffer);
235
236	vid_surfcachesize = D_SurfaceCacheForRes (vid.width, vid.height);
237
238	X11_buffersize += vid_surfcachesize;
239
240	d_pzbuffer = Hunk_HighAllocName (X11_buffersize, "video");
241	if (d_pzbuffer == NULL)
242		Sys_Error ("Not enough memory for video mode\n");
243
244	vid_surfcache = (byte *) d_pzbuffer
245		+ vid.width * vid.height * sizeof (*d_pzbuffer);
246
247	D_InitCaches(vid_surfcache, vid_surfcachesize);
248
249	pwidth = x_visinfo->depth / 8;
250	if (pwidth == 3) pwidth = 4;
251	mem = ((vid.width*pwidth+7)&~7) * vid.height;
252
253	x_framebuffer[0] = XCreateImage(	x_disp,
254		x_vis,
255		x_visinfo->depth,
256		ZPixmap,
257		0,
258		malloc(mem),
259		vid.width, vid.height,
260		32,
261		0);
262
263	if (!x_framebuffer[0])
264		Sys_Error("VID: XCreateImage failed\n");
265
266	vid.buffer = (byte*) (x_framebuffer[0]);
267	vid.conbuffer = vid.buffer;
268
269}
270
271void ResetSharedFrameBuffers(void)
272{
273
274	int size;
275	int key;
276	int minsize = getpagesize();
277	int frm;
278
279	if (d_pzbuffer)
280	{
281		D_FlushCaches ();
282		Hunk_FreeToHighMark (X11_highhunkmark);
283		d_pzbuffer = NULL;
284	}
285
286	X11_highhunkmark = Hunk_HighMark ();
287
288// alloc an extra line in case we want to wrap, and allocate the z-buffer
289	X11_buffersize = vid.width * vid.height * sizeof (*d_pzbuffer);
290
291	vid_surfcachesize = D_SurfaceCacheForRes (vid.width, vid.height);
292
293	X11_buffersize += vid_surfcachesize;
294
295	d_pzbuffer = Hunk_HighAllocName (X11_buffersize, "video");
296	if (d_pzbuffer == NULL)
297		Sys_Error ("Not enough memory for video mode\n");
298
299	vid_surfcache = (byte *) d_pzbuffer
300		+ vid.width * vid.height * sizeof (*d_pzbuffer);
301
302	D_InitCaches(vid_surfcache, vid_surfcachesize);
303
304	for (frm=0 ; frm<2 ; frm++)
305	{
306
307	// free up old frame buffer memory
308
309		if (x_framebuffer[frm])
310		{
311			XShmDetach(x_disp, &x_shminfo[frm]);
312			free(x_framebuffer[frm]);
313			shmdt(x_shminfo[frm].shmaddr);
314		}
315
316	// create the image
317
318		x_framebuffer[frm] = XShmCreateImage(	x_disp,
319						x_vis,
320						x_visinfo->depth,
321						ZPixmap,
322						0,
323						&x_shminfo[frm],
324						vid.width,
325						vid.height );
326
327	// grab shared memory
328
329		size = x_framebuffer[frm]->bytes_per_line
330			* x_framebuffer[frm]->height;
331		if (size < minsize)
332			Sys_Error("VID: Window must use at least %d bytes\n", minsize);
333
334		key = random();
335		x_shminfo[frm].shmid = shmget((key_t)key, size, IPC_CREAT|0777);
336		if (x_shminfo[frm].shmid==-1)
337			Sys_Error("VID: Could not get any shared memory\n");
338
339		// attach to the shared memory segment
340		x_shminfo[frm].shmaddr =
341			(void *) shmat(x_shminfo[frm].shmid, 0, 0);
342
343		printf("VID: shared memory id=%d, addr=0x%lx\n", x_shminfo[frm].shmid,
344			(long) x_shminfo[frm].shmaddr);
345
346		x_framebuffer[frm]->data = x_shminfo[frm].shmaddr;
347
348	// get the X server to attach to it
349
350		if (!XShmAttach(x_disp, &x_shminfo[frm]))
351			Sys_Error("VID: XShmAttach() failed\n");
352		XSync(x_disp, 0);
353		shmctl(x_shminfo[frm].shmid, IPC_RMID, 0);
354
355	}
356
357}
358
359// Called at startup to set up translation tables, takes 256 8 bit RGB values
360// the palette data will go away after the call, so it must be copied off if
361// the video driver will need it again
362
363void	VID_Init (unsigned char *palette)
364{
365
366   int pnum, i;
367   XVisualInfo template;
368   int num_visuals;
369   int template_mask;
370
371	S_Init();
372
373   ignorenext=0;
374   vid.width = 320;
375   vid.height = 200;
376   vid.maxwarpwidth = WARP_WIDTH;
377   vid.maxwarpheight = WARP_HEIGHT;
378   vid.numpages = 2;
379   vid.colormap = host_colormap;
380   //	vid.cbits = VID_CBITS;
381   //	vid.grades = VID_GRADES;
382   vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));
383
384	srandom(getpid());
385
386	verbose=COM_CheckParm("-verbose");
387
388// open the display
389	x_disp = XOpenDisplay(0);
390	if (!x_disp)
391	{
392		if (getenv("DISPLAY"))
393			Sys_Error("VID: Could not open display [%s]\n",
394				getenv("DISPLAY"));
395		else
396			Sys_Error("VID: Could not open local display\n");
397	}
398
399// catch signals so i can turn on auto-repeat
400
401	{
402		struct sigaction sa;
403		sigaction(SIGINT, 0, &sa);
404		sa.sa_handler = TragicDeath;
405		sigaction(SIGINT, &sa, 0);
406		sigaction(SIGTERM, &sa, 0);
407	}
408
409	XAutoRepeatOff(x_disp);
410
411// for debugging only
412	XSynchronize(x_disp, True);
413
414// check for command-line window size
415	if ((pnum=COM_CheckParm("-winsize")))
416	{
417		if (pnum >= com_argc-2)
418			Sys_Error("VID: -winsize <width> <height>\n");
419		vid.width = Q_atoi(com_argv[pnum+1]);
420		vid.height = Q_atoi(com_argv[pnum+2]);
421		if (!vid.width || !vid.height)
422			Sys_Error("VID: Bad window width/height\n");
423	}
424	if ((pnum=COM_CheckParm("-width"))) {
425		if (pnum >= com_argc-1)
426			Sys_Error("VID: -width <width>\n");
427		vid.width = Q_atoi(com_argv[pnum+1]);
428		if (!vid.width)
429			Sys_Error("VID: Bad window width\n");
430	}
431	if ((pnum=COM_CheckParm("-height"))) {
432		if (pnum >= com_argc-1)
433			Sys_Error("VID: -height <height>\n");
434		vid.height = Q_atoi(com_argv[pnum+1]);
435		if (!vid.height)
436			Sys_Error("VID: Bad window height\n");
437	}
438
439	template_mask = 0;
440
441// specify a visual id
442	if ((pnum=COM_CheckParm("-visualid")))
443	{
444		if (pnum >= com_argc-1)
445			Sys_Error("VID: -visualid <id#>\n");
446		template.visualid = Q_atoi(com_argv[pnum+1]);
447		template_mask = VisualIDMask;
448	}
449
450// If not specified, use default visual
451	else
452	{
453		int screen;
454		screen = XDefaultScreen(x_disp);
455		template.visualid =
456			XVisualIDFromVisual(XDefaultVisual(x_disp, screen));
457		template_mask = VisualIDMask;
458	}
459
460// pick a visual- warn if more than one was available
461	x_visinfo = XGetVisualInfo(x_disp, template_mask, &template, &num_visuals);
462	if (num_visuals > 1)
463	{
464		printf("Found more than one visual id at depth %d:\n", template.depth);
465		for (i=0 ; i<num_visuals ; i++)
466			printf("	-visualid %d\n", (int)(x_visinfo[i].visualid));
467	}
468	else if (num_visuals == 0)
469	{
470		if (template_mask == VisualIDMask)
471			Sys_Error("VID: Bad visual id %d\n", template.visualid);
472		else
473			Sys_Error("VID: No visuals at depth %d\n", template.depth);
474	}
475
476	if (verbose)
477	{
478		printf("Using visualid %d:\n", (int)(x_visinfo->visualid));
479		printf("	screen %d\n", x_visinfo->screen);
480		printf("	red_mask 0x%x\n", (int)(x_visinfo->red_mask));
481		printf("	green_mask 0x%x\n", (int)(x_visinfo->green_mask));
482		printf("	blue_mask 0x%x\n", (int)(x_visinfo->blue_mask));
483		printf("	colormap_size %d\n", x_visinfo->colormap_size);
484		printf("	bits_per_rgb %d\n", x_visinfo->bits_per_rgb);
485	}
486
487	x_vis = x_visinfo->visual;
488
489// setup attributes for main window
490	{
491	   int attribmask = CWEventMask  | CWColormap | CWBorderPixel;
492	   XSetWindowAttributes attribs;
493	   Colormap tmpcmap;
494
495	   tmpcmap = XCreateColormap(x_disp, XRootWindow(x_disp,
496							 x_visinfo->screen), x_vis, AllocNone);
497
498           attribs.event_mask = StructureNotifyMask | KeyPressMask
499	     | KeyReleaseMask | ExposureMask | PointerMotionMask |
500	     ButtonPressMask | ButtonReleaseMask;
501	   attribs.border_pixel = 0;
502	   attribs.colormap = tmpcmap;
503
504// create the main window
505		x_win = XCreateWindow(	x_disp,
506			XRootWindow(x_disp, x_visinfo->screen),
507			0, 0,	// x, y
508			vid.width, vid.height,
509			0, // borderwidth
510			x_visinfo->depth,
511			InputOutput,
512			x_vis,
513			attribmask,
514			&attribs );
515		XStoreName( x_disp,x_win,"xquake");
516
517
518		if (x_visinfo->class != TrueColor)
519			XFreeColormap(x_disp, tmpcmap);
520
521	}
522
523	if (x_visinfo->depth == 8)
524	{
525
526	// create and upload the palette
527		if (x_visinfo->class == PseudoColor)
528		{
529			x_cmap = XCreateColormap(x_disp, x_win, x_vis, AllocAll);
530			VID_SetPalette(palette);
531			XSetWindowColormap(x_disp, x_win, x_cmap);
532		}
533
534	}
535
536// inviso cursor
537	XDefineCursor(x_disp, x_win, CreateNullCursor(x_disp, x_win));
538
539// create the GC
540	{
541		XGCValues xgcvalues;
542		int valuemask = GCGraphicsExposures;
543		xgcvalues.graphics_exposures = False;
544		x_gc = XCreateGC(x_disp, x_win, valuemask, &xgcvalues );
545	}
546
547// map the window
548	XMapWindow(x_disp, x_win);
549
550// wait for first exposure event
551	{
552		XEvent event;
553		do
554		{
555			XNextEvent(x_disp, &event);
556			if (event.type == Expose && !event.xexpose.count)
557				oktodraw = true;
558		} while (!oktodraw);
559	}
560// now safe to draw
561
562// even if MITSHM is available, make sure it's a local connection
563	if (XShmQueryExtension(x_disp))
564	{
565		char *displayname;
566		doShm = true;
567		displayname = (char *) getenv("DISPLAY");
568		if (displayname)
569		{
570			char *d = displayname;
571			while (*d && (*d != ':')) d++;
572			if (*d) *d = 0;
573			if (!(!strcasecmp(displayname, "unix") || !*displayname))
574				doShm = false;
575		}
576	}
577
578	if (doShm)
579	{
580		x_shmeventtype = XShmGetEventBase(x_disp) + ShmCompletion;
581		ResetSharedFrameBuffers();
582	}
583	else
584		ResetFrameBuffer();
585
586	current_framebuffer = 0;
587	vid.rowbytes = x_framebuffer[0]->bytes_per_line;
588	vid.buffer = x_framebuffer[0]->data;
589	vid.direct = 0;
590	vid.conbuffer = x_framebuffer[0]->data;
591	vid.conrowbytes = vid.rowbytes;
592	vid.conwidth = vid.width;
593	vid.conheight = vid.height;
594	vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0);
595
596//	XSynchronize(x_disp, False);
597
598}
599
600void VID_ShiftPalette(unsigned char *p)
601{
602	VID_SetPalette(p);
603}
604
605
606
607void VID_SetPalette(unsigned char *palette)
608{
609
610	int i;
611	XColor colors[256];
612
613	for(i=0;i<256;i++)
614		st2d_8to16table[i]= xlib_rgb(palette[i*3],
615			palette[i*3+1],palette[i*3+2]);
616
617	if (x_visinfo->class == PseudoColor && x_visinfo->depth == 8)
618	{
619		if (palette != current_palette)
620			memcpy(current_palette, palette, 768);
621		for (i=0 ; i<256 ; i++)
622		{
623			colors[i].pixel = i;
624			colors[i].flags = DoRed|DoGreen|DoBlue;
625			colors[i].red = palette[i*3] * 257;
626			colors[i].green = palette[i*3+1] * 257;
627			colors[i].blue = palette[i*3+2] * 257;
628		}
629		XStoreColors(x_disp, x_cmap, colors, 256);
630	}
631
632}
633
634// Called at shutdown
635
636void	VID_Shutdown (void)
637{
638	Con_Printf("VID_Shutdown\n");
639	XAutoRepeatOn(x_disp);
640	XCloseDisplay(x_disp);
641}
642
643int XLateKey(XKeyEvent *ev)
644{
645
646	int key;
647	char buf[64];
648	KeySym keysym;
649
650	key = 0;
651
652	XLookupString(ev, buf, sizeof buf, &keysym, 0);
653
654	switch(keysym)
655	{
656		case XK_KP_Page_Up:
657		case XK_Page_Up:	 key = K_PGUP; break;
658
659		case XK_KP_Page_Down:
660		case XK_Page_Down:	 key = K_PGDN; break;
661
662		case XK_KP_Home:
663		case XK_Home:	 key = K_HOME; break;
664
665		case XK_KP_End:
666		case XK_End:	 key = K_END; break;
667
668		case XK_KP_Left:
669		case XK_Left:	 key = K_LEFTARROW; break;
670
671		case XK_KP_Right:
672		case XK_Right:	key = K_RIGHTARROW;		break;
673
674		case XK_KP_Down:
675		case XK_Down:	 key = K_DOWNARROW; break;
676
677		case XK_KP_Up:
678		case XK_Up:		 key = K_UPARROW;	 break;
679
680		case XK_Escape: key = K_ESCAPE;		break;
681
682		case XK_KP_Enter:
683		case XK_Return: key = K_ENTER;		 break;
684
685		case XK_Tab:		key = K_TAB;			 break;
686
687		case XK_F1:		 key = K_F1;				break;
688
689		case XK_F2:		 key = K_F2;				break;
690
691		case XK_F3:		 key = K_F3;				break;
692
693		case XK_F4:		 key = K_F4;				break;
694
695		case XK_F5:		 key = K_F5;				break;
696
697		case XK_F6:		 key = K_F6;				break;
698
699		case XK_F7:		 key = K_F7;				break;
700
701		case XK_F8:		 key = K_F8;				break;
702
703		case XK_F9:		 key = K_F9;				break;
704
705		case XK_F10:		key = K_F10;			 break;
706
707		case XK_F11:		key = K_F11;			 break;
708
709		case XK_F12:		key = K_F12;			 break;
710
711		case XK_BackSpace: key = K_BACKSPACE; break;
712
713		case XK_KP_Delete:
714		case XK_Delete: key = K_DEL; break;
715
716		case XK_Pause:	key = K_PAUSE;		 break;
717
718		case XK_Shift_L:
719		case XK_Shift_R:	key = K_SHIFT;		break;
720
721		case XK_Execute:
722		case XK_Control_L:
723		case XK_Control_R:	key = K_CTRL;		 break;
724
725		case XK_Alt_L:
726		case XK_Meta_L:
727		case XK_Alt_R:
728		case XK_Meta_R: key = K_ALT;			break;
729
730		case XK_KP_Begin: key = K_AUX30;	break;
731
732		case XK_Insert:
733		case XK_KP_Insert: key = K_INS; break;
734
735		case XK_KP_Multiply: key = '*'; break;
736		case XK_KP_Add: key = '+'; break;
737		case XK_KP_Subtract: key = '-'; break;
738		case XK_KP_Divide: key = '/'; break;
739
740#if 0
741		case 0x021: key = '1';break;/* [!] */
742		case 0x040: key = '2';break;/* [@] */
743		case 0x023: key = '3';break;/* [#] */
744		case 0x024: key = '4';break;/* [$] */
745		case 0x025: key = '5';break;/* [%] */
746		case 0x05e: key = '6';break;/* [^] */
747		case 0x026: key = '7';break;/* [&] */
748		case 0x02a: key = '8';break;/* [*] */
749		case 0x028: key = '9';;break;/* [(] */
750		case 0x029: key = '0';break;/* [)] */
751		case 0x05f: key = '-';break;/* [_] */
752		case 0x02b: key = '=';break;/* [+] */
753		case 0x07c: key = '\'';break;/* [|] */
754		case 0x07d: key = '[';break;/* [}] */
755		case 0x07b: key = ']';break;/* [{] */
756		case 0x022: key = '\'';break;/* ["] */
757		case 0x03a: key = ';';break;/* [:] */
758		case 0x03f: key = '/';break;/* [?] */
759		case 0x03e: key = '.';break;/* [>] */
760		case 0x03c: key = ',';break;/* [<] */
761#endif
762
763		default:
764			key = *(unsigned char*)buf;
765			if (key >= 'A' && key <= 'Z')
766				key = key - 'A' + 'a';
767//			fprintf(stdout, "case 0x0%x: key = ___;break;/* [%c] */\n", keysym);
768			break;
769	}
770
771	return key;
772}
773
774struct
775{
776	int key;
777	int down;
778} keyq[64];
779int keyq_head=0;
780int keyq_tail=0;
781
782int config_notify=0;
783int config_notify_width;
784int config_notify_height;
785
786void GetEvent(void)
787{
788	XEvent x_event;
789	int b;
790
791	XNextEvent(x_disp, &x_event);
792	switch(x_event.type) {
793	case KeyPress:
794		keyq[keyq_head].key = XLateKey(&x_event.xkey);
795		keyq[keyq_head].down = true;
796		keyq_head = (keyq_head + 1) & 63;
797		break;
798	case KeyRelease:
799		keyq[keyq_head].key = XLateKey(&x_event.xkey);
800		keyq[keyq_head].down = false;
801		keyq_head = (keyq_head + 1) & 63;
802		break;
803
804	case MotionNotify:
805		if (_windowed_mouse.value) {
806			mouse_x = (float) ((int)x_event.xmotion.x - (int)(vid.width/2));
807			mouse_y = (float) ((int)x_event.xmotion.y - (int)(vid.height/2));
808//printf("m: x=%d,y=%d, mx=%3.2f,my=%3.2f\n",
809//	x_event.xmotion.x, x_event.xmotion.y, mouse_x, mouse_y);
810
811			/* move the mouse to the window center again */
812			XSelectInput(x_disp,x_win,StructureNotifyMask|KeyPressMask
813				|KeyReleaseMask|ExposureMask
814				|ButtonPressMask
815				|ButtonReleaseMask);
816			XWarpPointer(x_disp,None,x_win,0,0,0,0,
817				(vid.width/2),(vid.height/2));
818			XSelectInput(x_disp,x_win,StructureNotifyMask|KeyPressMask
819				|KeyReleaseMask|ExposureMask
820				|PointerMotionMask|ButtonPressMask
821				|ButtonReleaseMask);
822		} else {
823			mouse_x = (float) (x_event.xmotion.x-p_mouse_x);
824			mouse_y = (float) (x_event.xmotion.y-p_mouse_y);
825			p_mouse_x=x_event.xmotion.x;
826			p_mouse_y=x_event.xmotion.y;
827		}
828		break;
829
830	case ButtonPress:
831		b=-1;
832		if (x_event.xbutton.button == 1)
833			b = 0;
834		else if (x_event.xbutton.button == 2)
835			b = 2;
836		else if (x_event.xbutton.button == 3)
837			b = 1;
838		if (b>=0)
839			mouse_buttonstate |= 1<<b;
840		break;
841
842	case ButtonRelease:
843		b=-1;
844		if (x_event.xbutton.button == 1)
845			b = 0;
846		else if (x_event.xbutton.button == 2)
847			b = 2;
848		else if (x_event.xbutton.button == 3)
849			b = 1;
850		if (b>=0)
851			mouse_buttonstate &= ~(1<<b);
852		break;
853
854	case ConfigureNotify:
855//printf("config notify\n");
856		config_notify_width = x_event.xconfigure.width;
857		config_notify_height = x_event.xconfigure.height;
858		config_notify = 1;
859		break;
860
861	default:
862		if (doShm && x_event.type == x_shmeventtype)
863			oktodraw = true;
864	}
865
866	if (old_windowed_mouse != _windowed_mouse.value) {
867		old_windowed_mouse = _windowed_mouse.value;
868
869		if (!_windowed_mouse.value) {
870			/* ungrab the pointer */
871			XUngrabPointer(x_disp,CurrentTime);
872		} else {
873			/* grab the pointer */
874			XGrabPointer(x_disp,x_win,True,0,GrabModeAsync,
875				GrabModeAsync,x_win,None,CurrentTime);
876		}
877	}
878}
879
880// flushes the given rectangles from the view buffer to the screen
881
882void	VID_Update (vrect_t *rects)
883{
884
885// if the window changes dimension, skip this frame
886
887	if (config_notify)
888	{
889		fprintf(stderr, "config notify\n");
890		config_notify = 0;
891		vid.width = config_notify_width & ~7;
892		vid.height = config_notify_height;
893		if (doShm)
894			ResetSharedFrameBuffers();
895		else
896			ResetFrameBuffer();
897		vid.rowbytes = x_framebuffer[0]->bytes_per_line;
898		vid.buffer = x_framebuffer[current_framebuffer]->data;
899		vid.conbuffer = vid.buffer;
900		vid.conwidth = vid.width;
901		vid.conheight = vid.height;
902		vid.conrowbytes = vid.rowbytes;
903		vid.recalc_refdef = 1;				// force a surface cache flush
904		Con_CheckResize();
905		Con_Clear_f();
906		return;
907	}
908
909	if (doShm)
910	{
911
912		while (rects)
913		{
914			if (x_visinfo->depth != 8)
915				st2_fixup( x_framebuffer[current_framebuffer],
916					rects->x, rects->y, rects->width,
917					rects->height);
918			if (!XShmPutImage(x_disp, x_win, x_gc,
919				x_framebuffer[current_framebuffer], rects->x, rects->y,
920				rects->x, rects->y, rects->width, rects->height, True))
921					Sys_Error("VID_Update: XShmPutImage failed\n");
922			oktodraw = false;
923			while (!oktodraw) GetEvent();
924			rects = rects->pnext;
925		}
926		current_framebuffer = !current_framebuffer;
927		vid.buffer = x_framebuffer[current_framebuffer]->data;
928		vid.conbuffer = vid.buffer;
929		XSync(x_disp, False);
930	}
931	else
932	{
933		while (rects)
934		{
935			if (x_visinfo->depth != 8)
936				st2_fixup( x_framebuffer[current_framebuffer],
937					rects->x, rects->y, rects->width,
938					rects->height);
939			XPutImage(x_disp, x_win, x_gc, x_framebuffer[0], rects->x,
940				rects->y, rects->x, rects->y, rects->width, rects->height);
941			rects = rects->pnext;
942		}
943		XSync(x_disp, False);
944	}
945
946}
947
948static int dither;
949
950void VID_DitherOn(void)
951{
952    if (dither == 0)
953    {
954		vid.recalc_refdef = 1;
955        dither = 1;
956    }
957}
958
959void VID_DitherOff(void)
960{
961    if (dither)
962    {
963		vid.recalc_refdef = 1;
964        dither = 0;
965    }
966}
967
968int Sys_OpenWindow(void)
969{
970	return 0;
971}
972
973void Sys_EraseWindow(int window)
974{
975}
976
977void Sys_DrawCircle(int window, int x, int y, int r)
978{
979}
980
981void Sys_DisplayWindow(int window)
982{
983}
984
985void Sys_SendKeyEvents(void)
986{
987// get events from x server
988	if (x_disp)
989	{
990		while (XPending(x_disp)) GetEvent();
991		while (keyq_head != keyq_tail)
992		{
993			Key_Event(keyq[keyq_tail].key, keyq[keyq_tail].down);
994			keyq_tail = (keyq_tail + 1) & 63;
995		}
996	}
997}
998
999#if 0
1000char *Sys_ConsoleInput (void)
1001{
1002
1003	static char	text[256];
1004	int		len;
1005	fd_set  readfds;
1006	int		ready;
1007	struct timeval timeout;
1008
1009	timeout.tv_sec = 0;
1010	timeout.tv_usec = 0;
1011	FD_ZERO(&readfds);
1012	FD_SET(0, &readfds);
1013	ready = select(1, &readfds, 0, 0, &timeout);
1014
1015	if (ready>0)
1016	{
1017		len = read (0, text, sizeof(text));
1018		if (len >= 1)
1019		{
1020			text[len-1] = 0;	// rip off the /n and terminate
1021			return text;
1022		}
1023	}
1024
1025	return 0;
1026
1027}
1028#endif
1029
1030void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
1031{
1032// direct drawing of the "accessing disk" icon isn't supported under Linux
1033}
1034
1035void D_EndDirectRect (int x, int y, int width, int height)
1036{
1037// direct drawing of the "accessing disk" icon isn't supported under Linux
1038}
1039
1040void IN_Init (void)
1041{
1042	Cvar_RegisterVariable (&_windowed_mouse);
1043	Cvar_RegisterVariable (&m_filter);
1044   if ( COM_CheckParm ("-nomouse") )
1045     return;
1046   mouse_x = mouse_y = 0.0;
1047   mouse_avail = 1;
1048}
1049
1050void IN_Shutdown (void)
1051{
1052   mouse_avail = 0;
1053}
1054
1055void IN_Commands (void)
1056{
1057	int i;
1058
1059	if (!mouse_avail) return;
1060
1061	for (i=0 ; i<mouse_buttons ; i++) {
1062		if ( (mouse_buttonstate & (1<<i)) && !(mouse_oldbuttonstate & (1<<i)) )
1063			Key_Event (K_MOUSE1 + i, true);
1064
1065		if ( !(mouse_buttonstate & (1<<i)) && (mouse_oldbuttonstate & (1<<i)) )
1066			Key_Event (K_MOUSE1 + i, false);
1067	}
1068	mouse_oldbuttonstate = mouse_buttonstate;
1069}
1070
1071void IN_Move (usercmd_t *cmd)
1072{
1073	if (!mouse_avail)
1074		return;
1075
1076	if (m_filter.value) {
1077		mouse_x = (mouse_x + old_mouse_x) * 0.5;
1078		mouse_y = (mouse_y + old_mouse_y) * 0.5;
1079	}
1080
1081	old_mouse_x = mouse_x;
1082	old_mouse_y = mouse_y;
1083
1084	mouse_x *= sensitivity.value;
1085	mouse_y *= sensitivity.value;
1086
1087	if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
1088		cmd->sidemove += m_side.value * mouse_x;
1089	else
1090		cl.viewangles[YAW] -= m_yaw.value * mouse_x;
1091	if (in_mlook.state & 1)
1092		V_StopPitchDrift ();
1093
1094	if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) {
1095		cl.viewangles[PITCH] += m_pitch.value * mouse_y;
1096		if (cl.viewangles[PITCH] > 80)
1097			cl.viewangles[PITCH] = 80;
1098		if (cl.viewangles[PITCH] < -70)
1099			cl.viewangles[PITCH] = -70;
1100	} else {
1101		if ((in_strafe.state & 1) && noclip_anglehack)
1102			cmd->upmove -= m_forward.value * mouse_y;
1103		else
1104			cmd->forwardmove -= m_forward.value * mouse_y;
1105	}
1106	mouse_x = mouse_y = 0.0;
1107}
1108
1109void VID_LockBuffer (void)
1110{
1111}
1112
1113void VID_UnlockBuffer (void)
1114{
1115}
1116
1117