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