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