|
|
1.1 root 1: // vid_x.c -- general x video driver
2:
3: #define _BSD
4:
5: #include <ctype.h>
6: #include <sys/time.h>
7: #include <sys/types.h>
8: #include <unistd.h>
9: #include <signal.h>
10: #include <stdlib.h>
11: #include <stdio.h>
12: #include <string.h>
13: #include <sys/ipc.h>
14: #include <sys/shm.h>
15: #include <X11/Xlib.h>
16: #include <X11/Xutil.h>
17: #include <X11/Xatom.h>
18: #include <X11/keysym.h>
19: #include <X11/extensions/XShm.h>
20:
21: #include "quakedef.h"
22: #include "d_local.h"
23:
24: typedef struct
25: {
26: int input;
27: int output;
28: } keymap_t;
29:
30: viddef_t vid; // global video state
31: unsigned short d_8to16table[256];
32:
33: int num_shades=32;
34:
35: int d_con_indirect = 0;
36:
37: int vid_buffersize;
38:
39: static qboolean doShm;
40: static Display *x_disp;
41: static Colormap x_cmap;
42: static Window x_win;
43: static GC x_gc;
44: static Visual *x_vis;
45: static XVisualInfo *x_visinfo;
46: //static XImage *x_image;
47:
48: static int x_shmeventtype;
49: //static XShmSegmentInfo x_shminfo;
50:
51: static qboolean oktodraw = false;
52:
53: int XShmQueryExtension(Display *);
54: int XShmGetEventBase(Display *);
55:
56: int current_framebuffer;
57: static XImage *x_framebuffer[2] = { 0, 0 };
58: static XShmSegmentInfo x_shminfo[2];
59:
60: static int verbose=0;
61:
62: static byte current_palette[768];
63:
64: static long X11_highhunkmark;
65: static long X11_buffersize;
66:
67: int vid_surfcachesize;
68: void *vid_surfcache;
69:
70: void (*vid_menudrawfn)(void);
71: void (*vid_menukeyfn)(int key);
72: void VID_MenuKey (int key);
73:
74: // ========================================================================
75: // Tragic death handler
76: // ========================================================================
77:
78: void TragicDeath(int signal_num)
79: {
80: XAutoRepeatOn(x_disp);
81: XCloseDisplay(x_disp);
82: Sys_Error("This death brought to you by the number %d\n", signal_num);
83: }
84:
85: // ========================================================================
86: // makes a null cursor
87: // ========================================================================
88:
89: static Cursor CreateNullCursor(Display *display, Window root)
90: {
91: Pixmap cursormask;
92: XGCValues xgc;
93: GC gc;
94: XColor dummycolour;
95: Cursor cursor;
96:
97: cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
98: xgc.function = GXclear;
99: gc = XCreateGC(display, cursormask, GCFunction, &xgc);
100: XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
101: dummycolour.pixel = 0;
102: dummycolour.red = 0;
103: dummycolour.flags = 04;
104: cursor = XCreatePixmapCursor(display, cursormask, cursormask,
105: &dummycolour,&dummycolour, 0,0);
106: XFreePixmap(display,cursormask);
107: XFreeGC(display,gc);
108: return cursor;
109: }
110:
111: void ResetFrameBuffer(void)
112: {
113:
114: int mem;
115: int pwidth;
116:
117: if (x_framebuffer[0])
118: {
119: free(x_framebuffer[0]->data);
120: free(x_framebuffer[0]);
121: }
122:
123: if (d_pzbuffer)
124: {
125: D_FlushCaches ();
126: Hunk_FreeToHighMark (X11_highhunkmark);
127: d_pzbuffer = NULL;
128: }
129: X11_highhunkmark = Hunk_HighMark ();
130:
131: // alloc an extra line in case we want to wrap, and allocate the z-buffer
132: X11_buffersize = vid.width * vid.height * sizeof (*d_pzbuffer);
133:
134: vid_surfcachesize = D_SurfaceCacheForRes (vid.width, vid.height);
135:
136: X11_buffersize += vid_surfcachesize;
137:
138: d_pzbuffer = Hunk_HighAllocName (X11_buffersize, "video");
139: if (d_pzbuffer == NULL)
140: Sys_Error ("Not enough memory for video mode\n");
141:
142: vid_surfcache = (byte *) d_pzbuffer
143: + vid.width * vid.height * sizeof (*d_pzbuffer);
144:
145: D_InitCaches(vid_surfcache, vid_surfcachesize);
146:
147: pwidth = x_visinfo->depth / 8;
148: if (pwidth == 3) pwidth = 4;
149: mem = ((vid.width*pwidth+7)&~7) * vid.height;
150:
151: x_framebuffer[0] = XCreateImage( x_disp,
152: x_vis,
153: x_visinfo->depth,
154: ZPixmap,
155: 0,
156: malloc(mem),
157: vid.width, vid.height,
158: 32,
159: 0);
160:
161: if (!x_framebuffer[0])
162: Sys_Error("VID: XCreateImage failed\n");
163:
164: vid.buffer = (byte*) (x_framebuffer[0]);
165: vid.conbuffer = vid.buffer;
166:
167: }
168:
169: void ResetSharedFrameBuffers(void)
170: {
171:
172: int size;
173: int key;
174: int minsize = getpagesize();
175: int frm;
176:
177: if (d_pzbuffer)
178: {
179: D_FlushCaches ();
180: Hunk_FreeToHighMark (X11_highhunkmark);
181: d_pzbuffer = NULL;
182: }
183:
184: X11_highhunkmark = Hunk_HighMark ();
185:
186: // alloc an extra line in case we want to wrap, and allocate the z-buffer
187: X11_buffersize = vid.width * vid.height * sizeof (*d_pzbuffer);
188:
189: vid_surfcachesize = D_SurfaceCacheForRes (vid.width, vid.height);
190:
191: X11_buffersize += vid_surfcachesize;
192:
193: d_pzbuffer = Hunk_HighAllocName (X11_buffersize, "video");
194: if (d_pzbuffer == NULL)
195: Sys_Error ("Not enough memory for video mode\n");
196:
197: vid_surfcache = (byte *) d_pzbuffer
198: + vid.width * vid.height * sizeof (*d_pzbuffer);
199:
200: D_InitCaches(vid_surfcache, vid_surfcachesize);
201:
202: for (frm=0 ; frm<2 ; frm++)
203: {
204:
205: // free up old frame buffer memory
206:
207: if (x_framebuffer[frm])
208: {
209: XShmDetach(x_disp, &x_shminfo[frm]);
210: free(x_framebuffer[frm]);
211: shmdt(x_shminfo[frm].shmaddr);
212: }
213:
214: // create the image
215:
216: x_framebuffer[frm] = XShmCreateImage( x_disp,
217: x_vis,
218: x_visinfo->depth,
219: ZPixmap,
220: 0,
221: &x_shminfo[frm],
222: vid.width,
223: vid.height );
224:
225: // grab shared memory
226:
227: size = x_framebuffer[frm]->bytes_per_line
228: * x_framebuffer[frm]->height;
229: if (size < minsize)
230: Sys_Error("VID: Window must use at least %d bytes\n", minsize);
231:
232: key = random();
233: x_shminfo[frm].shmid = shmget((key_t)key, size, IPC_CREAT|0777);
234: if (x_shminfo[frm].shmid==-1)
235: Sys_Error("VID: Could not get any shared memory\n");
236:
237: // attach to the shared memory segment
238: x_shminfo[frm].shmaddr =
239: (void *) shmat(x_shminfo[frm].shmid, 0, 0);
240:
241: printf("VID: shared memory id=%d, addr=0x%lx\n", x_shminfo[frm].shmid,
242: (long) x_shminfo[frm].shmaddr);
243:
244: x_framebuffer[frm]->data = x_shminfo[frm].shmaddr;
245:
246: // get the X server to attach to it
247:
248: if (!XShmAttach(x_disp, &x_shminfo[frm]))
249: Sys_Error("VID: XShmAttach() failed\n");
250: XSync(x_disp, 0);
251: shmctl(x_shminfo[frm].shmid, IPC_RMID, 0);
252:
253: }
254:
255: }
256:
257: // Called at startup to set up translation tables, takes 256 8 bit RGB values
258: // the palette data will go away after the call, so it must be copied off if
259: // the video driver will need it again
260:
261: void VID_Init (unsigned char *palette)
262: {
263:
264: int pnum, i;
265: XVisualInfo template;
266: int num_visuals;
267: int template_mask;
268:
269: vid.width = 320;
270: vid.height = 200;
271: vid.maxwarpwidth = WARP_WIDTH;
272: vid.maxwarpheight = WARP_HEIGHT;
273: vid.numpages = 2;
274: vid.colormap = host_colormap;
275: // vid.cbits = VID_CBITS;
276: // vid.grades = VID_GRADES;
277: vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));
278:
279: srandom(getpid());
280:
281: verbose=COM_CheckParm("-verbose");
282:
283: // open the display
284: x_disp = XOpenDisplay(0);
285: if (!x_disp)
286: {
287: if (getenv("DISPLAY"))
288: Sys_Error("VID: Could not open display [%s]\n",
289: getenv("DISPLAY"));
290: else
291: Sys_Error("VID: Could not open local display\n");
292: }
293:
294: // catch signals so i can turn on auto-repeat
295:
296: {
297: struct sigaction sa;
298: sigaction(SIGINT, 0, &sa);
299: sa.sa_handler = TragicDeath;
300: sigaction(SIGINT, &sa, 0);
301: sigaction(SIGTERM, &sa, 0);
302: }
303:
304: XAutoRepeatOff(x_disp);
305:
306: // for debugging only
307: XSynchronize(x_disp, True);
308:
309: // check for command-line window size
310: if ((pnum=COM_CheckParm("-winsize")))
311: {
312: if (pnum >= com_argc-2)
313: Sys_Error("VID: -winsize <width> <height>\n");
314: vid.width = Q_atoi(com_argv[pnum+1]);
315: vid.height = Q_atoi(com_argv[pnum+2]);
316: if (!vid.width || !vid.height)
317: Sys_Error("VID: Bad window width/height\n");
318: }
319:
320: template_mask = 0;
321:
322: // specify a visual id
323: if ((pnum=COM_CheckParm("-visualid")))
324: {
325: if (pnum >= com_argc-1)
326: Sys_Error("VID: -visualid <id#>\n");
327: template.visualid = Q_atoi(com_argv[pnum+1]);
328: template_mask = VisualIDMask;
329: }
330:
331: // If not specified, use default visual
332: else
333: {
334: int screen;
335: screen = XDefaultScreen(x_disp);
336: template.visualid =
337: XVisualIDFromVisual(XDefaultVisual(x_disp, screen));
338: template_mask = VisualIDMask;
339: }
340:
341: // pick a visual- warn if more than one was available
342: x_visinfo = XGetVisualInfo(x_disp, template_mask, &template, &num_visuals);
343: if (num_visuals > 1)
344: {
345: printf("Found more than one visual id at depth %d:\n", template.depth);
346: for (i=0 ; i<num_visuals ; i++)
347: printf(" -visualid %d\n", (int)(x_visinfo[i].visualid));
348: }
349: else if (num_visuals == 0)
350: {
351: if (template_mask == VisualIDMask)
352: Sys_Error("VID: Bad visual id %d\n", template.visualid);
353: else
354: Sys_Error("VID: No visuals at depth %d\n", template.depth);
355: }
356:
357: if (verbose)
358: {
359: printf("Using visualid %d:\n", (int)(x_visinfo->visualid));
360: printf(" screen %d\n", x_visinfo->screen);
361: printf(" red_mask 0x%x\n", (int)(x_visinfo->red_mask));
362: printf(" green_mask 0x%x\n", (int)(x_visinfo->green_mask));
363: printf(" blue_mask 0x%x\n", (int)(x_visinfo->blue_mask));
364: printf(" colormap_size %d\n", x_visinfo->colormap_size);
365: printf(" bits_per_rgb %d\n", x_visinfo->bits_per_rgb);
366: }
367:
368: x_vis = x_visinfo->visual;
369:
370: // setup attributes for main window
371: {
372: int attribmask = CWEventMask | CWColormap | CWBorderPixel;
373: XSetWindowAttributes attribs;
374: Colormap tmpcmap;
375:
376: tmpcmap = XCreateColormap(x_disp, XRootWindow(x_disp,
377: x_visinfo->screen), x_vis, AllocNone);
378:
379: attribs.event_mask = StructureNotifyMask | KeyPressMask
380: | KeyReleaseMask | ExposureMask;
381: attribs.border_pixel = 0;
382: attribs.colormap = tmpcmap;
383:
384: // create the main window
385: x_win = XCreateWindow( x_disp,
386: XRootWindow(x_disp, x_visinfo->screen),
387: 0, 0, // x, y
388: vid.width, vid.height,
389: 0, // borderwidth
390: x_visinfo->depth,
391: InputOutput,
392: x_vis,
393: attribmask,
394: &attribs );
395:
396: if (x_visinfo->class != TrueColor)
397: XFreeColormap(x_disp, tmpcmap);
398:
399: }
400:
401: if (x_visinfo->depth == 8)
402: {
403:
404: // create and upload the palette
405: if (x_visinfo->class == PseudoColor)
406: {
407: x_cmap = XCreateColormap(x_disp, x_win, x_vis, AllocAll);
408: VID_SetPalette(palette);
409: XSetWindowColormap(x_disp, x_win, x_cmap);
410: }
411:
412: }
413:
414: // inviso cursor
415: XDefineCursor(x_disp, x_win, CreateNullCursor(x_disp, x_win));
416:
417: // create the GC
418: {
419: XGCValues xgcvalues;
420: int valuemask = GCGraphicsExposures;
421: xgcvalues.graphics_exposures = False;
422: x_gc = XCreateGC(x_disp, x_win, valuemask, &xgcvalues );
423: }
424:
425: // map the window
426: XMapWindow(x_disp, x_win);
427:
428: // wait for first exposure event
429: {
430: XEvent event;
431: do
432: {
433: XNextEvent(x_disp, &event);
434: if (event.type == Expose && !event.xexpose.count)
435: oktodraw = true;
436: } while (!oktodraw);
437: }
438: // now safe to draw
439:
440: // even if MITSHM is available, make sure it's a local connection
441: if (XShmQueryExtension(x_disp))
442: {
443: char *displayname;
444: doShm = true;
445: displayname = (char *) getenv("DISPLAY");
446: if (displayname)
447: {
448: char *d = displayname;
449: while (*d && (*d != ':')) d++;
450: if (*d) *d = 0;
451: if (!(!strcasecmp(displayname, "unix") || !*displayname))
452: doShm = false;
453: }
454: }
455:
456: if (doShm)
457: {
458: x_shmeventtype = XShmGetEventBase(x_disp) + ShmCompletion;
459: ResetSharedFrameBuffers();
460: }
461: else
462: ResetFrameBuffer();
463:
464: current_framebuffer = 0;
465: vid.rowbytes = x_framebuffer[0]->bytes_per_line;
466: vid.buffer = x_framebuffer[0]->data;
467: vid.direct = 0;
468: vid.conbuffer = x_framebuffer[0]->data;
469: vid.conrowbytes = vid.rowbytes;
470: vid.conwidth = vid.width;
471: vid.conheight = vid.height;
472: vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0);
473:
474: // XSynchronize(x_disp, False);
475:
476: }
477:
478: void VID_ShiftPalette(unsigned char *p)
479: {
480: VID_SetPalette(p);
481: }
482:
483: void VID_SetPalette(unsigned char *palette)
484: {
485:
486: int i;
487: XColor colors[256];
488:
489: if (x_visinfo->class == PseudoColor && x_visinfo->depth == 8)
490: {
491: if (palette != current_palette)
492: memcpy(current_palette, palette, 768);
493: for (i=0 ; i<256 ; i++)
494: {
495: colors[i].pixel = i;
496: colors[i].flags = DoRed|DoGreen|DoBlue;
497: colors[i].red = palette[i*3] * 257;
498: colors[i].green = palette[i*3+1] * 257;
499: colors[i].blue = palette[i*3+2] * 257;
500: }
501: XStoreColors(x_disp, x_cmap, colors, 256);
502: }
503:
504: }
505:
506: // Called at shutdown
507:
508: void VID_Shutdown (void)
509: {
510: Con_Printf("VID_Shutdown\n");
511: XAutoRepeatOn(x_disp);
512: XCloseDisplay(x_disp);
513: }
514:
515: int XLateKey(XKeyEvent *ev)
516: {
517:
518: int key;
519: char buf[64];
520: KeySym keysym;
521:
522: key = 0;
523:
524: XLookupString(ev, buf, sizeof buf, &keysym, 0);
525:
526: switch(keysym)
527: {
528: case XK_Page_Up: key = K_PGUP; break;
529: case XK_Page_Down: key = K_PGDN; break;
530: case XK_Home: key = K_HOME; break;
531: case XK_End: key = K_END; break;
532: case XK_Left: key = K_LEFTARROW; break;
533: case XK_Right: key = K_RIGHTARROW; break;
534: case XK_Down: key = K_DOWNARROW; break;
535: case XK_Up: key = K_UPARROW; break;
536: case XK_Escape: key = K_ESCAPE; break;
537: case XK_Return: key = K_ENTER; break;
538: case XK_Tab: key = K_TAB; break;
539: case XK_F1: key = K_F1; break;
540: case XK_F2: key = K_F2; break;
541: case XK_F3: key = K_F3; break;
542: case XK_F4: key = K_F4; break;
543: case XK_F5: key = K_F5; break;
544: case XK_F6: key = K_F6; break;
545: case XK_F7: key = K_F7; break;
546: case XK_F8: key = K_F8; break;
547: case XK_F9: key = K_F9; break;
548: case XK_F10: key = K_F10; break;
549: case XK_F11: key = K_F11; break;
550: case XK_F12: key = K_F12; break;
551: case XK_BackSpace:
552: case XK_Delete: key = K_BACKSPACE; break;
553: case XK_Pause: key = K_PAUSE; break;
554: case XK_Shift_L:
555: case XK_Shift_R: key = K_SHIFT; break;
556: case XK_Execute:
557: case XK_Control_L:
558: case XK_Control_R: key = K_CTRL; break;
559: case XK_Alt_L:
560: case XK_Meta_L:
561: case XK_Alt_R:
562: case XK_Meta_R: key = K_ALT; break;
563:
564: case 0x021: key = '1';break;/* [!] */
565: case 0x040: key = '2';break;/* [@] */
566: case 0x023: key = '3';break;/* [#] */
567: case 0x024: key = '4';break;/* [$] */
568: case 0x025: key = '5';break;/* [%] */
569: case 0x05e: key = '6';break;/* [^] */
570: case 0x026: key = '7';break;/* [&] */
571: case 0x02a: key = '8';break;/* [*] */
572: case 0x028: key = '9';;break;/* [(] */
573: case 0x029: key = '0';break;/* [)] */
574: case 0x05f: key = '-';break;/* [_] */
575: case 0x02b: key = '=';break;/* [+] */
576: case 0x07c: key = '\'';break;/* [|] */
577: case 0x07d: key = '[';break;/* [}] */
578: case 0x07b: key = ']';break;/* [{] */
579: case 0x022: key = '\'';break;/* ["] */
580: case 0x03a: key = ';';break;/* [:] */
581: case 0x03f: key = '/';break;/* [?] */
582: case 0x03e: key = '.';break;/* [>] */
583: case 0x03c: key = ',';break;/* [<] */
584:
585: default:
586: key = *(unsigned char*)buf;
587: if (key >= 'A' && key <= 'Z')
588: key = key - 'A' + 'a';
589: // fprintf(stderr, "case 0x0%x: key = ___;break;/* [%c] */\n", keysym);
590: break;
591: }
592:
593: return key;
594:
595: }
596:
597: struct
598: {
599: int key;
600: int down;
601: } keyq[64];
602: int keyq_head=0;
603: int keyq_tail=0;
604:
605: int config_notify=0;
606: int config_notify_width;
607: int config_notify_height;
608:
609: void GetEvent(void)
610: {
611:
612: XEvent x_event;
613:
614: XNextEvent(x_disp, &x_event);
615: switch(x_event.type)
616: {
617: case KeyPress:
618: keyq[keyq_head].key = XLateKey(&x_event.xkey);
619: keyq[keyq_head].down = true;
620: keyq_head = (keyq_head + 1) & 63;
621: break;
622: case KeyRelease:
623: keyq[keyq_head].key = XLateKey(&x_event.xkey);
624: keyq[keyq_head].down = false;
625: keyq_head = (keyq_head + 1) & 63;
626: break;
627: case ConfigureNotify:
628: // printf("config notify\n");
629: config_notify_width = x_event.xconfigure.width;
630: config_notify_height = x_event.xconfigure.height;
631: config_notify = 1;
632: break;
633: default:
634: if (doShm && x_event.type == x_shmeventtype)
635: oktodraw = true;
636: }
637:
638: }
639:
640: // flushes the given rectangles from the view buffer to the screen
641:
642: void VID_Update (vrect_t *rects)
643: {
644:
645: // if the window changes dimension, skip this frame
646:
647: if (config_notify)
648: {
649: fprintf(stderr, "config notify\n");
650: config_notify = 0;
651: vid.width = config_notify_width & ~7;
652: vid.height = config_notify_height;
653: if (doShm)
654: ResetSharedFrameBuffers();
655: else
656: ResetFrameBuffer();
657: vid.rowbytes = x_framebuffer[0]->bytes_per_line;
658: vid.buffer = x_framebuffer[current_framebuffer]->data;
659: vid.conbuffer = vid.buffer;
660: vid.conwidth = vid.width;
661: vid.conheight = vid.height;
662: vid.conrowbytes = vid.rowbytes;
663: vid.recalc_refdef = 1; // force a surface cache flush
664: Con_CheckResize();
665: Con_Clear_f();
666: return;
667: }
668:
669: if (doShm)
670: {
671:
672: while (rects)
673: {
674: if (!XShmPutImage(x_disp, x_win, x_gc,
675: x_framebuffer[current_framebuffer], rects->x, rects->y,
676: rects->x, rects->y, rects->width, rects->height, True))
677: Sys_Error("VID_Update: XShmPutImage failed\n");
678: oktodraw = false;
679: while (!oktodraw) GetEvent();
680: rects = rects->pnext;
681: }
682: current_framebuffer = !current_framebuffer;
683: vid.buffer = x_framebuffer[current_framebuffer]->data;
684: vid.conbuffer = vid.buffer;
685: XSync(x_disp, False);
686: }
687: else
688: {
689: while (rects)
690: {
691: XPutImage(x_disp, x_win, x_gc, x_framebuffer[0], rects->x,
692: rects->y, rects->x, rects->y, rects->width, rects->height);
693: rects = rects->pnext;
694: }
695: XSync(x_disp, False);
696: }
697:
698: }
699:
700: static int dither;
701:
702: void VID_DitherOn(void)
703: {
704: if (dither == 0)
705: {
706: vid.recalc_refdef = 1;
707: dither = 1;
708: }
709: }
710:
711: void VID_DitherOff(void)
712: {
713: if (dither)
714: {
715: vid.recalc_refdef = 1;
716: dither = 0;
717: }
718: }
719:
720: int Sys_OpenWindow(void)
721: {
722: return 0;
723: }
724:
725: void Sys_EraseWindow(int window)
726: {
727: }
728:
729: void Sys_DrawCircle(int window, int x, int y, int r)
730: {
731: }
732:
733: void Sys_DisplayWindow(int window)
734: {
735: }
736:
737: void Sys_SendKeyEvents(void)
738: {
739: // get events from x server
740: if (x_disp)
741: {
742: while (XPending(x_disp)) GetEvent();
743: while (keyq_head != keyq_tail)
744: {
745: Key_Event(keyq[keyq_tail].key, keyq[keyq_tail].down);
746: keyq_tail = (keyq_tail + 1) & 63;
747: }
748: }
749: }
750:
751: char *Sys_ConsoleInput (void)
752: {
753:
754: static char text[256];
755: int len;
756: fd_set readfds;
757: int ready;
758: struct timeval timeout;
759:
760: timeout.tv_sec = 0;
761: timeout.tv_usec = 0;
762: FD_ZERO(&readfds);
763: FD_SET(0, &readfds);
764: ready = select(1, &readfds, 0, 0, &timeout);
765:
766: if (ready>0)
767: {
768: len = read (0, text, sizeof(text));
769: if (len >= 1)
770: {
771: text[len-1] = 0; // rip off the /n and terminate
772: return text;
773: }
774: }
775:
776: return 0;
777:
778: }
779:
780: void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
781: {
782: // direct drawing of the "accessing disk" icon isn't supported under Linux
783: }
784:
785: void D_EndDirectRect (int x, int y, int width, int height)
786: {
787: // direct drawing of the "accessing disk" icon isn't supported under Linux
788: }
789:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.