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