1 /** EMULib Emulation Library *********************************/
2 /**                                                         **/
3 /**                        LibUnix.c                        **/
4 /**                                                         **/
5 /** This file contains Unix-dependent implementation        **/
6 /** parts of the emulation library.                         **/
7 /**                                                         **/
8 /** Copyright (C) Marat Fayzullin 1996-2009                 **/
9 /**     You are not allowed to distribute this software     **/
10 /**     commercially. Please, notify me, if you make any    **/
11 /**     changes to this file.                               **/
12 /*************************************************************/
13 #include "EMULib.h"
14 #include "Sound.h"
15 
16 #include <ctype.h>
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <sys/time.h>
23 
24 #ifdef MITSHM
25 #include <sys/ipc.h>
26 #include <sys/shm.h>
27 #endif
28 
29 #define FPS_COLOR PIXEL(255,0,255)
30 
31 extern int MasterSwitch; /* Switches to turn channels on/off */
32 extern int MasterVolume; /* Master volume                    */
33 
34 static volatile int TimerReady = 0;   /* 1: Sync timer ready */
35 static volatile unsigned int JoyState = 0; /* Joystick state */
36 static volatile unsigned int LastKey  = 0; /* Last key prsd  */
37 static volatile unsigned int KeyModes = 0; /* SHIFT/CTRL/ALT */
38 
39 static int Effects    = EFF_SCALE|EFF_SAVECPU; /* EFF_* bits */
40 static int TimerON    = 0; /* 1: sync timer is running       */
41 static Display *Dsp   = 0; /* X11 display                    */
42 static Screen *Scr    = 0; /* X11 screen                     */
43 static Window Wnd     = 0; /* X11 window                     */
44 static Colormap CMap;      /* X11 color map                  */
45 static Image OutImg;       /* Scaled output image buffer     */
46 static const char *AppTitle; /* Current window title         */
47 static int XSize,YSize;    /* Current window dimensions      */
48 
49 static int FrameCount;      /* Frame counter for EFF_SHOWFPS */
50 static int FrameRate;       /* Last frame rate value         */
51 static struct timeval TimeStamp; /* Last timestamp           */
52 
53 /** TimerHandler() *******************************************/
54 /** The main timer handler used by SetSyncTimer().          **/
55 /*************************************************************/
TimerHandler(int Arg)56 static void TimerHandler(int Arg)
57 {
58   /* Mark sync timer as "ready" */
59   TimerReady=1;
60   /* Repeat signal next time */
61   signal(Arg,TimerHandler);
62 }
63 
64 /** InitUnix() ***********************************************/
65 /** Initialize Unix/X11 resources and set initial window    **/
66 /** title and dimensions.                                   **/
67 /*************************************************************/
InitUnix(const char * Title,int Width,int Height)68 int InitUnix(const char *Title,int Width,int Height)
69 {
70   /* Initialize variables */
71   AppTitle    = Title;
72   XSize       = Width;
73   YSize       = Height;
74   TimerON     = 0;
75   TimerReady  = 0;
76   JoyState    = 0;
77   LastKey     = 0;
78   KeyModes    = 0;
79   Wnd         = 0;
80   Dsp         = 0;
81   Scr         = 0;
82   CMap        = 0;
83   FrameCount  = 0;
84   FrameRate   = 0;
85 
86   /* Get initial timestamp */
87   gettimeofday(&TimeStamp,0);
88 
89   /* No output image yet */
90   OutImg.XImg            = 0;
91 #ifdef MITSHM
92   OutImg.SHMInfo.shmaddr = 0;
93 #endif
94 
95   /* Open X11 display */
96   if(!(Dsp=XOpenDisplay(0))) return(0);
97 
98   /* Get default screen and color map */
99   Scr  = DefaultScreenOfDisplay(Dsp);
100   CMap = DefaultColormapOfScreen(Scr);
101 
102   /* Done */
103   return(1);
104 }
105 
106 /** TrashUnix() **********************************************/
107 /** Free resources allocated in InitUnix()                  **/
108 /*************************************************************/
TrashUnix(void)109 void TrashUnix(void)
110 {
111   /* Remove sync timer */
112   SetSyncTimer(0);
113   /* Shut down audio */
114   TrashAudio();
115   /* Free output image buffer */
116   FreeImage(&OutImg);
117 
118   /* If X11 display open... */
119   if(Dsp)
120   {
121     /* Close the window */
122     if(Wnd) { XDestroyWindow(Dsp,Wnd);Wnd=0; }
123     /* Done with display */
124     XCloseDisplay(Dsp);
125     /* Display now closed */
126     Dsp=0;
127   }
128 }
129 
130 /** ShowVideo() **********************************************/
131 /** Show "active" image at the actual screen or window.     **/
132 /*************************************************************/
ShowVideo(void)133 int ShowVideo(void)
134 {
135   Image *Output;
136   int SX,SY;
137 
138   /* Must have active video image, X11 display */
139   if(!Dsp||!VideoImg||!VideoImg->Data) return(0);
140 
141   /* If no window yet... */
142   if(!Wnd)
143   {
144     /* Create new window */
145     Wnd=X11Window(AppTitle? AppTitle:"EMULib",XSize,YSize);
146     if(!Wnd) return(0);
147   }
148 
149   /* Allocate image buffer if none */
150   if(!OutImg.Data&&!NewImage(&OutImg,XSize,YSize)) return(0);
151 
152   /* Wait for all X11 requests to complete, to avoid flicker */
153   XSync(Dsp,False);
154 
155   /* If not scaling or post-processing image, avoid extra work */
156   if(!(Effects&(EFF_SOFTEN|EFF_SCALE|EFF_TVLINES)))
157   {
158 #ifdef MITSHM
159     if(VideoImg->Attrs&EFF_MITSHM)
160       XShmPutImage(Dsp,Wnd,DefaultGCOfScreen(Scr),VideoImg->XImg,VideoX,VideoY,(XSize-VideoW)>>1,(YSize-VideoH)>>1,VideoW,VideoH,False);
161     else
162 #endif
163       XPutImage(Dsp,Wnd,DefaultGCOfScreen(Scr),VideoImg->XImg,VideoX,VideoY,(XSize-VideoW)>>1,(YSize-VideoH)>>1,VideoW,VideoH);
164     return(1);
165   }
166 
167   /* By default, we will be showing OutImg */
168   Output  = &OutImg;
169   SX      = 0;
170   SY      = 0;
171 
172   if(Effects&EFF_SOFTEN)
173   {
174     /* Apply softening */
175     SoftenImage(&OutImg,VideoImg,VideoX,VideoY,VideoW,VideoH);
176     /* Apply TV scanlines, if needed */
177     if(Effects&EFF_TVLINES)
178       TelevizeImage(&OutImg,0,0,OutImg.W,OutImg.H);
179   }
180   else if(Effects&EFF_TVLINES)
181   {
182     if(Effects&EFF_SCALE)
183     {
184       /* Scale VideoImg into OutImg */
185       ScaleImage(&OutImg,VideoImg,VideoX,VideoY,VideoW,VideoH);
186       /* Apply TV scanlines */
187       TelevizeImage(&OutImg,0,0,OutImg.W,OutImg.H);
188     }
189     else
190     {
191       /* Center VideoImg in OutImg */
192       IMGCopy(&OutImg,(OutImg.W-VideoW)>>1,(OutImg.H-VideoH)>>1,VideoImg,VideoX,VideoY,VideoW,VideoH,-1);
193       /* Apply TV scanlines */
194       TelevizeImage(&OutImg,(OutImg.W-VideoW)>>1,(OutImg.H-VideoH)>>1,VideoW,VideoH);
195     }
196   }
197   else if((OutImg.W==VideoW)&&(OutImg.H==VideoH))
198   {
199     /* Use VideoImg directly */
200     Output = VideoImg;
201     SX     = VideoX;
202     SY     = VideoY;
203   }
204   else if(Effects&EFF_SCALE)
205   {
206     /* Scale VideoImg to OutImg */
207     ScaleImage(&OutImg,VideoImg,VideoX,VideoY,VideoW,VideoH);
208   }
209   else if((OutImg.W<=VideoW)&&(OutImg.H<=VideoH))
210   {
211     /* Use VideoImg directly */
212     Output = VideoImg;
213     SX     = VideoX+((VideoW-OutImg.W)>>1);
214     SY     = VideoY+((VideoH-OutImg.H)>>1);
215   }
216   else
217   {
218     /* Center VideoImg in OutImg */
219     IMGCopy(&OutImg,(OutImg.W-VideoW)>>1,(OutImg.H-VideoH)>>1,VideoImg,VideoX,VideoY,VideoW,VideoH,-1);
220   }
221 
222   /* Show framerate if requested */
223   if((Effects&EFF_SHOWFPS)&&(FrameRate>0))
224   {
225     char S[8];
226     sprintf(S,"%dfps",FrameRate);
227     PrintXY(
228       OutImg,S,
229       ((OutImg.W-VideoW)>>1)+8,((OutImg.H-VideoH)>>1)+8,
230       FPS_COLOR,-1
231     );
232   }
233 
234   /* Wait for sync timer if requested */
235   if(Effects&EFF_SYNC) WaitSyncTimer();
236 
237   /* Copy image to the window, either using SHM or not */
238 #ifdef MITSHM
239   if(VideoImg->Attrs&EFF_MITSHM)
240     XShmPutImage(Dsp,Wnd,DefaultGCOfScreen(Scr),Output->XImg,SX,SY,0,0,XSize,YSize,False);
241   else
242 #endif
243     XPutImage(Dsp,Wnd,DefaultGCOfScreen(Scr),Output->XImg,SX,SY,0,0,XSize,YSize);
244 
245   /* Done */
246   return(1);
247 }
248 
249 /** GetJoystick() ********************************************/
250 /** Get the state of joypad buttons (1="pressed"). Refer to **/
251 /** the BTN_* #defines for the button mappings.             **/
252 /*************************************************************/
GetJoystick(void)253 unsigned int GetJoystick(void)
254 {
255   /* Count framerate */
256   if((Effects&EFF_SHOWFPS)&&(++FrameCount>=300))
257   {
258     struct timeval NewTS;
259     int Time;
260 
261     gettimeofday(&NewTS,0);
262     Time       = (NewTS.tv_sec-TimeStamp.tv_sec)*1000
263                + (NewTS.tv_usec-TimeStamp.tv_usec)/1000;
264     FrameRate  = 1000*FrameCount/(Time>0? Time:1);
265     TimeStamp  = NewTS;
266     FrameCount = 0;
267     FrameRate  = FrameRate>999? 999:FrameRate;
268   }
269 
270   /* Process any pending events */
271   X11ProcessEvents();
272 
273   /* Return current joystick state */
274   return(JoyState|KeyModes);
275 }
276 
277 /** GetMouse() ***********************************************/
278 /** Get mouse position and button states in the following   **/
279 /** format: RMB.LMB.Y[29-16].X[15-0].                       **/
280 /*************************************************************/
GetMouse(void)281 unsigned int GetMouse(void)
282 {
283   int X,Y,J,Mask;
284   Window W;
285 
286   /* Need to have a display and a window */
287   if(!Dsp||!Wnd) return(0);
288 
289   /* Query mouse pointer */
290   if(!XQueryPointer(Dsp,Wnd,&W,&W,&J,&J,&X,&Y,&Mask)) return(0);
291 
292   /* If scaling video... */
293   if(Effects&(EFF_SOFTEN|EFF_SCALE|EFF_TVLINES))
294   {
295     /* Scale mouse position */
296     X = VideoW*(X<0? 0:X>=XSize? XSize-1:X)/XSize;
297     Y = VideoH*(Y<0? 0:Y>=YSize? YSize-1:Y)/YSize;
298   }
299   else
300   {
301     /* Translate mouse position */
302     X-= ((XSize-VideoW)>>1);
303     Y-= ((YSize-VideoH)>>1);
304     X = X<0? 0:X>=XSize? XSize-1:X;
305     Y = Y<0? 0:Y>=YSize? YSize-1:Y;
306   }
307 
308   /* Return mouse position and buttons */
309   return(
310     (X&0xFFFF)
311   | ((Y&0x3FFF)<<16)
312   | (Mask&Button1Mask? 0x40000000:0)
313   | (Mask&Button3Mask? 0x80000000:0)
314   );
315 }
316 
317 /** GetKey() *************************************************/
318 /** Get currently pressed key or 0 if none pressed. Returns **/
319 /** CON_* definitions for arrows and special keys.          **/
320 /*************************************************************/
GetKey(void)321 unsigned int GetKey(void)
322 {
323   unsigned int J;
324 
325   X11ProcessEvents();
326   J=LastKey;
327   LastKey=0;
328   return(J);
329 }
330 
331 /** WaitKey() ************************************************/
332 /** Wait for a key to be pressed. Returns CON_* definitions **/
333 /** for arrows and special keys.                            **/
334 /*************************************************************/
WaitKey(void)335 unsigned int WaitKey(void)
336 {
337   unsigned int J;
338 
339   /* Swallow current keypress */
340   GetKey();
341   /* Wait in 100ms increments for a new keypress */
342   while(!(J=GetKey())&&VideoImg) usleep(100000);
343   /* Return key code */
344   return(J);
345 }
346 
347 /** WaitKeyOrMouse() *****************************************/
348 /** Wait for a key or a mouse button to be pressed. Returns **/
349 /** the same result as GetMouse(). If no mouse buttons      **/
350 /** reported to be pressed, do GetKey() to fetch a key.     **/
351 /*************************************************************/
WaitKeyOrMouse(void)352 unsigned int WaitKeyOrMouse(void)
353 {
354   unsigned int J;
355 
356   /* Swallow current keypress */
357   GetKey();
358   /* Make sure mouse keys are not pressed */
359   while(GetMouse()&0xC0000000) usleep(100000);
360   /* Wait in 100ms increments for a key or mouse click */
361   while(!(J=GetKey())&&!(GetMouse()&0xC0000000)&&VideoImg) usleep(100000);
362   /* Place key back into the buffer and return mouse state */
363   LastKey=J;
364   return(GetMouse());
365 }
366 
367 /** WaitSyncTimer() ******************************************/
368 /** Wait for the timer to become ready.                     **/
369 /*************************************************************/
WaitSyncTimer(void)370 void WaitSyncTimer(void)
371 {
372   /* Wait in 1ms increments until timer becomes ready */
373   while(!TimerReady&&TimerON&&VideoImg) usleep(1000);
374   /* Warn of missed timer events */
375   if((TimerReady>1)&&(Effects&EFF_VERBOSE))
376     printf("WaitSyncTimer(): Missed %d timer events.\n",TimerReady-1);
377   /* Reset timer */
378   TimerReady=0;
379 }
380 
381 /** SyncTimerReady() *****************************************/
382 /** Return 1 if sync timer ready, 0 otherwise.              **/
383 /*************************************************************/
SyncTimerReady(void)384 int SyncTimerReady(void)
385 {
386   /* Return whether timer is ready or not */
387   return(TimerReady||!TimerON||!VideoImg);
388 }
389 
390 /** SetSyncTimer() *******************************************/
391 /** Set synchronization timer to a given frequency in Hz.   **/
392 /*************************************************************/
SetSyncTimer(int Hz)393 int SetSyncTimer(int Hz)
394 {
395   struct itimerval TimerValue;
396 
397   /* Compute and set timer period */
398   TimerValue.it_interval.tv_sec  =
399   TimerValue.it_value.tv_sec     = 0;
400   TimerValue.it_interval.tv_usec =
401   TimerValue.it_value.tv_usec    = Hz? 1000000L/Hz:0;
402 
403   /* Set timer */
404   if(setitimer(ITIMER_REAL,&TimerValue,NULL)) return(0);
405 
406   /* Set timer signal */
407   signal(SIGALRM,Hz? TimerHandler:SIG_DFL);
408 
409   /* Done */
410   TimerON=Hz;
411   return(1);
412 }
413 
414 /** ChangeDir() **********************************************/
415 /** This function is a wrapper for chdir().                 **/
416 /*************************************************************/
ChangeDir(const char * Name)417 int ChangeDir(const char *Name) { return(chdir(Name)); }
418 
419 /** NewImage() ***********************************************/
420 /** Create a new image of the given size. Returns pointer   **/
421 /** to the image data on success, 0 on failure.             **/
422 /*************************************************************/
NewImage(Image * Img,int Width,int Height)423 pixel *NewImage(Image *Img,int Width,int Height)
424 {
425   XVisualInfo VInfo;
426   int Depth,J,I;
427 
428   /* Set data fields to ther defaults */
429   Img->Data    = 0;
430   Img->W       = 0;
431   Img->H       = 0;
432   Img->L       = 0;
433   Img->D       = 0;
434   Img->Attrs   = 0;
435   Img->Cropped = 0;
436 
437   /* Need to initalize library first */
438   if(!Dsp) return(0);
439 
440   /* Image depth we are going to use */
441   Depth = Effects&EFF_VARBPP? DefaultDepthOfScreen(Scr):(sizeof(pixel)<<3);
442 
443   /* Get appropriate Visual for this depth */
444   I=XScreenNumberOfScreen(Scr);
445   for(J=7;J>=0;J--)
446     if(XMatchVisualInfo(Dsp,I,Depth,J,&VInfo)) break;
447   if(J<0) return(0);
448 
449 #ifdef MITSHM
450   if(Effects&EFF_MITSHM)
451   {
452     /* Create shared XImage */
453     Img->XImg = XShmCreateImage(Dsp,VInfo.visual,Depth,ZPixmap,0,&Img->SHMInfo,Width,Height);
454     if(!Img->XImg) return(0);
455 
456     /* Get ID for shared segment */
457     Img->SHMInfo.shmid = shmget(IPC_PRIVATE,Img->XImg->bytes_per_line*Img->XImg->height,IPC_CREAT|0777);
458     if(Img->SHMInfo.shmid==-1) { XDestroyImage(Img->XImg);return(0); }
459 
460     /* Attach to shared segment by ID */
461     Img->XImg->data = Img->SHMInfo.shmaddr = shmat(Img->SHMInfo.shmid,0,0);
462     if(!Img->XImg->data)
463     {
464       shmctl(Img->SHMInfo.shmid,IPC_RMID,0);
465       XDestroyImage(Img->XImg);
466       return(0);
467     }
468 
469     /* Can write into shared segment */
470     Img->SHMInfo.readOnly = False;
471 
472     /* Attach segment to X display and make sure it is done */
473     J=XShmAttach(Dsp,&Img->SHMInfo);
474     XSync(Dsp,False);
475 
476     /* We do not need an ID any longer */
477     shmctl(Img->SHMInfo.shmid,IPC_RMID,0);
478 
479     /* If attachment failed, break out */
480     if(!J)
481     {
482       shmdt(Img->SHMInfo.shmaddr);
483       XDestroyImage(Img->XImg);
484       return(0);
485     }
486   }
487   else
488 #endif
489   {
490     /* Create normal XImage */
491     Img->XImg = XCreateImage(Dsp,VInfo.visual,Depth,ZPixmap,0,0,Width,Height,Depth,0);
492     if(!Img->XImg) return(0);
493 
494     /* Allocate data */
495     Img->XImg->data = (char *)malloc(Img->XImg->bytes_per_line*Img->XImg->height);
496     if(!Img->XImg->data) { XDestroyImage(Img->XImg);return(0); }
497   }
498 
499   /* Done */
500   Depth      = Depth==24? 32:Depth;
501   Img->Data  = (pixel *)Img->XImg->data;
502   Img->W     = Img->XImg->width;
503   Img->H     = Img->XImg->height;
504   Img->L     = Img->XImg->bytes_per_line/(Depth>>3);
505   Img->D     = Depth;
506   Img->Attrs = Effects&(EFF_MITSHM|EFF_VARBPP);
507   return(Img->Data);
508 }
509 
510 /** FreeImage() **********************************************/
511 /** Free previously allocated image.                        **/
512 /*************************************************************/
FreeImage(Image * Img)513 void FreeImage(Image *Img)
514 {
515   /* Need to initalize library first */
516   if(!Dsp||!Img->Data) return;
517 
518 #ifdef MITSHM
519   /* Detach shared memory segment */
520   if((Img->Attrs&EFF_MITSHM)&&Img->SHMInfo.shmaddr)
521   { XShmDetach(Dsp,&Img->SHMInfo);shmdt(Img->SHMInfo.shmaddr); }
522   Img->SHMInfo.shmaddr = 0;
523 #endif
524 
525   /* Get rid of the image */
526   if(Img->XImg) { XDestroyImage(Img->XImg);Img->XImg=0; }
527 
528   /* Image freed */
529   Img->Data = 0;
530   Img->W    = 0;
531   Img->H    = 0;
532   Img->L    = 0;
533 }
534 
535 /** CropImage() **********************************************/
536 /** Create a subimage Dst of the image Src. Returns Dst.    **/
537 /*************************************************************/
CropImage(Image * Dst,const Image * Src,int X,int Y,int W,int H)538 Image *CropImage(Image *Dst,const Image *Src,int X,int Y,int W,int H)
539 {
540   Dst->Data    = (pixel *)(Src->Data+Src->L*Y+X);
541   Dst->Cropped = 1;
542   Dst->W       = W;
543   Dst->H       = H;
544   Dst->L       = Src->L;
545   Dst->D       = Src->D;
546   Dst->XImg    = 0;
547   Dst->Attrs   = 0;
548   return(Dst);
549 }
550 
551 /** SetVideo() ***********************************************/
552 /** Set part of the image as "active" for display.          **/
553 /*************************************************************/
SetVideo(Image * Img,int X,int Y,int W,int H)554 void SetVideo(Image *Img,int X,int Y,int W,int H)
555 {
556   /* If video exists, modify its size */
557   if(Dsp&&VideoW&&VideoH)
558   {
559     int DW,DH,SW,SH;
560 
561     /* Make sure window dimensions stay at ~1:1 ratio */
562     DW    = W/H>1? W/(W/H):W;
563     DH    = H/W>1? H/(H/W):H;
564     SW    = VideoW/VideoH>1? VideoW/(VideoW/VideoH):VideoW;
565     SH    = VideoH/VideoW>1? VideoH/(VideoH/VideoW):VideoH;
566     XSize = XSize*DW/SW;
567     YSize = YSize*DH/SH;
568 
569     if(Wnd) XResizeWindow(Dsp,Wnd,XSize,YSize);
570     FreeImage(&OutImg);
571   }
572 
573   /* Call default SetVideo() function */
574   GenericSetVideo(Img,X,Y,W,H);
575 }
576 
577 /** X11SetEffects() ******************************************/
578 /** Set visual effects applied to video in ShowVideo().     **/
579 /*************************************************************/
X11SetEffects(int NewEffects)580 void X11SetEffects(int NewEffects)
581 {
582   /* Set new effects */
583   Effects=NewEffects;
584 }
585 
586 /** X11ProcessEvents() ***************************************/
587 /** Process X11 event messages.                             **/
588 /*************************************************************/
X11ProcessEvents(void)589 void X11ProcessEvents(void)
590 {
591   XEvent E;
592   int J;
593 
594   /* Need to have display and a window */
595   if(!Dsp||!Wnd) return;
596 
597   /* Check for keypresses/keyreleases */
598   while(XCheckWindowEvent(Dsp,Wnd,KeyPressMask|KeyReleaseMask,&E))
599   {
600     /* Get key code */
601     J=XLookupKeysym((XKeyEvent *)&E,0);
602 
603     /* If key pressed... */
604     if(E.type==KeyPress)
605     {
606       /* Process ASCII keys */
607       if((J>=' ')&&(J<0x7F)) LastKey=toupper(J);
608 
609       /* Special keys pressed... */
610       switch(J)
611       {
612         case XK_Left:         JoyState|=BTN_LEFT;LastKey=CON_LEFT;break;
613         case XK_Right:        JoyState|=BTN_RIGHT;LastKey=CON_RIGHT;break;
614         case XK_Up:           JoyState|=BTN_UP;LastKey=CON_UP;break;
615         case XK_Down:         JoyState|=BTN_DOWN;LastKey=CON_DOWN;break;
616         case XK_Shift_L:
617         case XK_Shift_R:      KeyModes|=CON_SHIFT;break;
618         case XK_Alt_L:
619         case XK_Alt_R:        KeyModes|=CON_ALT;break;
620         case XK_Control_L:
621         case XK_Control_R:    KeyModes|=CON_CONTROL;break;
622         case XK_Caps_Lock:    KeyModes|=CON_CAPS;break;
623         case XK_Escape:       JoyState|=BTN_EXIT;LastKey=CON_EXIT;break;
624         case XK_Tab:          JoyState|=BTN_SELECT;break;
625         case XK_Return:       JoyState|=BTN_START;LastKey=CON_OK;break;
626         case XK_BackSpace:    LastKey=8;break;
627 
628         case 'q': case 'e': case 't': case 'u': case 'o':
629           JoyState|=BTN_FIREL;break;
630         case 'w': case 'r': case 'y': case 'i': case 'p':
631           JoyState|=BTN_FIRER;break;
632         case 'a': case 's': case 'd': case 'f': case 'g':
633         case 'h': case 'j': case 'k': case 'l': case ' ':
634           JoyState|=BTN_FIREA;break;
635         case 'z': case 'x': case 'c': case 'v': case 'b':
636         case 'n': case 'm':
637           JoyState|=BTN_FIREB;break;
638 
639         case XK_Page_Up:
640           if(KeyModes&CON_ALT)
641           {
642             /* Volume up */
643             SetChannels(MasterVolume<247? MasterVolume+8:255,MasterSwitch);
644             /* Key swallowed */
645             J=0;
646           }
647           break;
648 
649         case XK_Page_Down:
650           if(KeyModes&CON_ALT)
651           {
652             /* Volume down */
653             SetChannels(MasterVolume>8? MasterVolume-8:0,MasterSwitch);
654             /* Key swallowed */
655             J=0;
656           }
657           break;
658       }
659 
660       /* Call user handler */
661       if(J&&KeyHandler) (*KeyHandler)(J|KeyModes);
662     }
663 
664     /* If key released... */
665     if(E.type==KeyRelease)
666     {
667       /* Special keys released... */
668       switch(J)
669       {
670         case XK_Left:         JoyState&=~BTN_LEFT;break;
671         case XK_Right:        JoyState&=~BTN_RIGHT;break;
672         case XK_Up:           JoyState&=~BTN_UP;break;
673         case XK_Down:         JoyState&=~BTN_DOWN;break;
674         case XK_Shift_L:
675         case XK_Shift_R:      KeyModes&=~CON_SHIFT;break;
676         case XK_Alt_L:
677         case XK_Alt_R:        KeyModes&=~CON_ALT;break;
678         case XK_Control_L:
679         case XK_Control_R:    KeyModes&=~CON_CONTROL;break;
680         case XK_Caps_Lock:    KeyModes&=~CON_CAPS;break;
681         case XK_Escape:       JoyState&=~BTN_EXIT;break;
682         case XK_Tab:          JoyState&=~BTN_SELECT;break;
683         case XK_Return:       JoyState&=~BTN_START;break;
684 
685         case 'q': case 'e': case 't': case 'u': case 'o':
686           JoyState&=~BTN_FIREL;break;
687         case 'w': case 'r': case 'y': case 'i': case 'p':
688           JoyState&=~BTN_FIRER;break;
689         case 'a': case 's': case 'd': case 'f': case 'g':
690         case 'h': case 'j': case 'k': case 'l': case ' ':
691           JoyState&=~BTN_FIREA;break;
692         case 'z': case 'x': case 'c': case 'v': case 'b':
693         case 'n': case 'm':
694           JoyState&=~BTN_FIREB;break;
695       }
696 
697       /* Call user handler */
698       if(J&&KeyHandler) (*KeyHandler)(J|CON_RELEASE|KeyModes);
699     }
700   }
701 
702   /* Check for mouse clicks, but only one at a time */
703   if(XCheckWindowEvent(Dsp,Wnd,ButtonPressMask|ButtonReleaseMask,&E))
704   {
705     (*MouseHandler)(E.xbutton.x, E.xbutton.y, E.type==ButtonPress?1:0);
706   }
707 
708   /* Check for focus change events */
709   for(E.type=0;XCheckWindowEvent(Dsp,Wnd,FocusChangeMask,&E););
710   /* If saving CPU and focus is out... */
711   if((Effects&EFF_SAVECPU)&&(E.type==FocusOut))
712   {
713     /* Pause audio */
714     J=MasterSwitch;
715     SetChannels(MasterVolume,0);
716     PauseAudio(1);
717     /* Wait for focus-in event */
718     do
719       while(!XCheckWindowEvent(Dsp,Wnd,FocusChangeMask,&E)&&VideoImg) sleep(1);
720     while((E.type!=FocusIn)&&VideoImg);
721     /* Resume audio */
722     PauseAudio(0);
723     SetChannels(MasterVolume,J);
724   }
725 
726   /* If window has been resized, remove current output buffer */
727   for(E.type=0;XCheckWindowEvent(Dsp,Wnd,StructureNotifyMask,&E););
728   if((E.type==ConfigureNotify)&&!E.xconfigure.send_event)
729     if((XSize!=E.xconfigure.width)||(YSize!=E.xconfigure.height))
730     {
731       FreeImage(&OutImg);
732       XSize=E.xconfigure.width;
733       YSize=E.xconfigure.height;
734     }
735 }
736 
737 /** X11GetColor **********************************************/
738 /** Get pixel for the current screen depth based on the RGB **/
739 /** values.                                                 **/
740 /*************************************************************/
X11GetColor(unsigned char R,unsigned char G,unsigned char B)741 unsigned int X11GetColor(unsigned char R,unsigned char G,unsigned char B)
742 {
743   int J;
744 
745   /* If using constant BPP, just return a pixel */
746   if(!Dsp||!(Effects&EFF_VARBPP)) return(PIXEL(R,G,B));
747 
748   /* If variable BPP, compute pixel based on the screen depth */
749   J=DefaultDepthOfScreen(Scr);
750   return(
751     J<=8?  (((7*(R)/255)<<5)|((7*(G)/255)<<2)|(3*(B)/255))
752   : J<=16? (((31*(R)/255)<<11)|((63*(G)/255)<<5)|(31*(B)/255))
753   : J<=32? (((R)<<16)|((G)<<8)|B)
754   : 0
755   );
756 }
757 
758 /** X11Window() **********************************************/
759 /** Open a window of a given size with a given title.       **/
760 /*************************************************************/
X11Window(const char * Title,int Width,int Height)761 Window X11Window(const char *Title,int Width,int Height)
762 {
763   XSetWindowAttributes Attrs;
764   XClassHint ClassHint;
765   XSizeHints Hints;
766   XWMHints WMHints;
767   Window Wnd;
768   char *P;
769   int Q;
770 
771   /* Need to initalize library first */
772   if(!Dsp) return(0);
773 
774   /* Set necessary attributes */
775   Attrs.event_mask =
776     ButtonPressMask|ButtonReleaseMask|FocusChangeMask
777     |KeyPressMask|KeyReleaseMask|StructureNotifyMask;
778   Attrs.background_pixel=BlackPixelOfScreen(Scr);
779   Attrs.backing_store=Always;
780 
781   /* Create a window */
782   Wnd=XCreateWindow
783       (
784         Dsp,RootWindowOfScreen(Scr),0,0,Width,Height,1,
785         CopyFromParent,CopyFromParent,CopyFromParent,
786         CWBackPixel|CWEventMask|CWBackingStore,&Attrs
787       );
788   if(!Wnd) return(0);
789 
790   /* Set application class hint */
791   if(ARGC&&ARGV)
792   {
793     P=strrchr(ARGV[0],'/');
794     ClassHint.res_name  = P? P+1:ARGV[0];
795     ClassHint.res_class = P? P+1:ARGV[0];
796     XSetClassHint(Dsp,Wnd,&ClassHint);
797     XSetCommand(Dsp,Wnd,ARGV,ARGC);
798   }
799 
800   /* Set hints */
801   Q=sizeof(long);
802   Hints.flags       = PSize|PMinSize|PMaxSize|PResizeInc;
803   Hints.min_width   = ((Width/4)/Q)*Q;
804   Hints.max_width   = ((Width*4)/Q)*Q;
805   Hints.base_width  = (Width/Q)*Q;
806   Hints.width_inc   = Q;
807   Hints.min_height  = ((Height/4)/Q)*Q;
808   Hints.max_height  = ((Height*4)/Q)*Q;
809   Hints.base_height = (Height/Q)*Q;
810   Hints.height_inc  = Q;
811   WMHints.input     = True;
812   WMHints.flags     = InputHint;
813 
814   if(ARGC&&ARGV)
815   {
816     WMHints.window_group=Wnd;
817     WMHints.flags|=WindowGroupHint;
818   }
819 
820   /* Set hints, title, size */
821   XSetWMHints(Dsp,Wnd,&WMHints);
822   XSetWMNormalHints(Dsp,Wnd,&Hints);
823   XStoreName(Dsp,Wnd,Title);
824 
825   /* Do additional housekeeping and return */
826   XMapRaised(Dsp,Wnd);
827   XClearWindow(Dsp,Wnd);
828 
829   /* Done */
830   return(Wnd);
831 }
832