1 /***********************************************************************************
2   Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 
4   (c) Copyright 1996 - 2002  Gary Henderson (gary.henderson@ntlworld.com),
5                              Jerremy Koot (jkoot@snes9x.com)
6 
7   (c) Copyright 2002 - 2004  Matthew Kendora
8 
9   (c) Copyright 2002 - 2005  Peter Bortas (peter@bortas.org)
10 
11   (c) Copyright 2004 - 2005  Joel Yliluoma (http://iki.fi/bisqwit/)
12 
13   (c) Copyright 2001 - 2006  John Weidman (jweidman@slip.net)
14 
15   (c) Copyright 2002 - 2006  funkyass (funkyass@spam.shaw.ca),
16                              Kris Bleakley (codeviolation@hotmail.com)
17 
18   (c) Copyright 2002 - 2010  Brad Jorsch (anomie@users.sourceforge.net),
19                              Nach (n-a-c-h@users.sourceforge.net),
20 
21   (c) Copyright 2002 - 2011  zones (kasumitokoduck@yahoo.com)
22 
23   (c) Copyright 2006 - 2007  nitsuja
24 
25   (c) Copyright 2009 - 2016  BearOso,
26                              OV2
27 
28 
29   BS-X C emulator code
30   (c) Copyright 2005 - 2006  Dreamer Nom,
31                              zones
32 
33   C4 x86 assembler and some C emulation code
34   (c) Copyright 2000 - 2003  _Demo_ (_demo_@zsnes.com),
35                              Nach,
36                              zsKnight (zsknight@zsnes.com)
37 
38   C4 C++ code
39   (c) Copyright 2003 - 2006  Brad Jorsch,
40                              Nach
41 
42   DSP-1 emulator code
43   (c) Copyright 1998 - 2006  _Demo_,
44                              Andreas Naive (andreasnaive@gmail.com),
45                              Gary Henderson,
46                              Ivar (ivar@snes9x.com),
47                              John Weidman,
48                              Kris Bleakley,
49                              Matthew Kendora,
50                              Nach,
51                              neviksti (neviksti@hotmail.com)
52 
53   DSP-2 emulator code
54   (c) Copyright 2003         John Weidman,
55                              Kris Bleakley,
56                              Lord Nightmare (lord_nightmare@users.sourceforge.net),
57                              Matthew Kendora,
58                              neviksti
59 
60   DSP-3 emulator code
61   (c) Copyright 2003 - 2006  John Weidman,
62                              Kris Bleakley,
63                              Lancer,
64                              z80 gaiden
65 
66   DSP-4 emulator code
67   (c) Copyright 2004 - 2006  Dreamer Nom,
68                              John Weidman,
69                              Kris Bleakley,
70                              Nach,
71                              z80 gaiden
72 
73   OBC1 emulator code
74   (c) Copyright 2001 - 2004  zsKnight,
75                              pagefault (pagefault@zsnes.com),
76                              Kris Bleakley
77                              Ported from x86 assembler to C by sanmaiwashi
78 
79   SPC7110 and RTC C++ emulator code used in 1.39-1.51
80   (c) Copyright 2002         Matthew Kendora with research by
81                              zsKnight,
82                              John Weidman,
83                              Dark Force
84 
85   SPC7110 and RTC C++ emulator code used in 1.52+
86   (c) Copyright 2009         byuu,
87                              neviksti
88 
89   S-DD1 C emulator code
90   (c) Copyright 2003         Brad Jorsch with research by
91                              Andreas Naive,
92                              John Weidman
93 
94   S-RTC C emulator code
95   (c) Copyright 2001 - 2006  byuu,
96                              John Weidman
97 
98   ST010 C++ emulator code
99   (c) Copyright 2003         Feather,
100                              John Weidman,
101                              Kris Bleakley,
102                              Matthew Kendora
103 
104   Super FX x86 assembler emulator code
105   (c) Copyright 1998 - 2003  _Demo_,
106                              pagefault,
107                              zsKnight
108 
109   Super FX C emulator code
110   (c) Copyright 1997 - 1999  Ivar,
111                              Gary Henderson,
112                              John Weidman
113 
114   Sound emulator code used in 1.5-1.51
115   (c) Copyright 1998 - 2003  Brad Martin
116   (c) Copyright 1998 - 2006  Charles Bilyue'
117 
118   Sound emulator code used in 1.52+
119   (c) Copyright 2004 - 2007  Shay Green (gblargg@gmail.com)
120 
121   S-SMP emulator code used in 1.54+
122   (c) Copyright 2016         byuu
123 
124   SH assembler code partly based on x86 assembler code
125   (c) Copyright 2002 - 2004  Marcus Comstedt (marcus@mc.pp.se)
126 
127   2xSaI filter
128   (c) Copyright 1999 - 2001  Derek Liauw Kie Fa
129 
130   HQ2x, HQ3x, HQ4x filters
131   (c) Copyright 2003         Maxim Stepin (maxim@hiend3d.com)
132 
133   NTSC filter
134   (c) Copyright 2006 - 2007  Shay Green
135 
136   GTK+ GUI code
137   (c) Copyright 2004 - 2016  BearOso
138 
139   Win32 GUI code
140   (c) Copyright 2003 - 2006  blip,
141                              funkyass,
142                              Matthew Kendora,
143                              Nach,
144                              nitsuja
145   (c) Copyright 2009 - 2016  OV2
146 
147   Mac OS GUI code
148   (c) Copyright 1998 - 2001  John Stiles
149   (c) Copyright 2001 - 2011  zones
150 
151 
152   Specific ports contains the works of other authors. See headers in
153   individual files.
154 
155 
156   Snes9x homepage: http://www.snes9x.com/
157 
158   Permission to use, copy, modify and/or distribute Snes9x in both binary
159   and source form, for non-commercial purposes, is hereby granted without
160   fee, providing that this license information and copyright notice appear
161   with all copies and any derived work.
162 
163   This software is provided 'as-is', without any express or implied
164   warranty. In no event shall the authors be held liable for any damages
165   arising from the use of this software or it's derivatives.
166 
167   Snes9x is freeware for PERSONAL USE only. Commercial users should
168   seek permission of the copyright holders first. Commercial use includes,
169   but is not limited to, charging money for Snes9x or software derived from
170   Snes9x, including Snes9x or derivatives in commercial game bundles, and/or
171   using Snes9x as a promotion for your commercial product.
172 
173   The copyright holders request that bug fixes and improvements to the code
174   should be forwarded to them so everyone can benefit from the modifications
175   in future versions.
176 
177   Super NES and Super Nintendo Entertainment System are trademarks of
178   Nintendo Co., Limited and its subsidiary companies.
179  ***********************************************************************************/
180 
181 
182 #include <stdlib.h>
183 #include <unistd.h>
184 #include <ctype.h>
185 #include <fcntl.h>
186 #include <errno.h>
187 #include <string.h>
188 #ifdef HAVE_STRINGS_H
189 #include <strings.h>
190 #endif
191 #include <sys/stat.h>
192 #include <sys/ioctl.h>
193 
194 #include <X11/Xlib.h>
195 #include <X11/Xutil.h>
196 #include <X11/Xatom.h>
197 #include <X11/keysym.h>
198 #include <X11/cursorfont.h>
199 
200 #ifdef USE_XVIDEO
201 #include <X11/extensions/Xvlib.h>
202 
203 #define FOURCC_YUY2 0x32595559
204 #endif
205 
206 #ifdef MITSHM
207 #include <sys/ipc.h>
208 #include <sys/shm.h>
209 #include <X11/extensions/XShm.h>
210 #endif
211 
212 #include "snes9x.h"
213 #include "memmap.h"
214 #include "ppu.h"
215 #include "controls.h"
216 #include "movie.h"
217 #include "logger.h"
218 #include "conffile.h"
219 #include "blit.h"
220 #include "display.h"
221 
222 // Wrapper struct to make generic XvImage vs XImage
223 struct Image
224 {
225 #ifdef USE_XVIDEO
226 	union
227 	{
228 		XvImage*	xvimage;
229 #endif
230 		XImage*	ximage;
231 #ifdef USE_XVIDEO
232 	};
233 #endif
234 
235 	char *data;
236 
237 	uint32 height;
238 	uint32 data_size;
239 	uint32 bits_per_pixel;
240 	uint32 bytes_per_line;
241 };
242 
243 struct GUIData
244 {
245 	Display			*display;
246 	Screen			*screen;
247 	Visual			*visual;
248 	GC				gc;
249 	int				screen_num;
250 	int				depth;
251 	int				pixel_format;
252 	int				bytes_per_pixel;
253 	uint32			red_shift;
254 	uint32			blue_shift;
255 	uint32			green_shift;
256 	uint32			red_size;
257 	uint32			green_size;
258 	uint32			blue_size;
259 	Window			window;
260 	Image			*image;
261 	uint8			*snes_buffer;
262 	uint8			*filter_buffer;
263 	uint8			*blit_screen;
264 	uint32			blit_screen_pitch;
265 	bool8			need_convert;
266 	Cursor			point_cursor;
267 	Cursor			cross_hair_cursor;
268 	int				video_mode;
269 	int				mouse_x;
270 	int				mouse_y;
271 	bool8			mod1_pressed;
272 	bool8			no_repeat;
273 	bool8			fullscreen;
274 	int				x_offset;
275 	int				y_offset;
276 #ifdef USE_XVIDEO
277 	bool8			use_xvideo;
278 	int				xv_port;
279 	int				scale_w;
280 	int				scale_h;
281 
282 	bool8			maxaspect;
283 	int			imageHeight;
284 
285 	int				xv_format;
286 	int				xv_bpp;
287 	unsigned char		y_table[1 << 15];
288 	unsigned char		u_table[1 << 15];
289 	unsigned char		v_table[1 << 15];
290 #endif
291 #ifdef MITSHM
292 	XShmSegmentInfo	sm_info;
293 	bool8			use_shared_memory;
294 #endif
295 };
296 
297 static struct GUIData	GUI;
298 
299 typedef	std::pair<std::string, std::string>	strpair_t;
300 extern	std::vector<strpair_t>				keymaps;
301 
302 typedef	void (* Blitter) (uint8 *, int, uint8 *, int, int, int);
303 
304 #ifdef __linux
305 // Select seems to be broken in 2.x.x kernels - if a signal interrupts a
306 // select system call with a zero timeout, the select call is restarted but
307 // with an infinite timeout! The call will block until data arrives on the
308 // selected fd(s).
309 //
310 // The workaround is to stop the X library calling select in the first
311 // place! Replace XPending - which polls for data from the X server using
312 // select - with an ioctl call to poll for data and then only call the blocking
313 // XNextEvent if data is waiting.
314 #define SELECT_BROKEN_FOR_SIGNALS
315 #endif
316 
317 enum
318 {
319 	VIDEOMODE_BLOCKY = 1,
320 	VIDEOMODE_TV,
321 	VIDEOMODE_SMOOTH,
322 	VIDEOMODE_SUPEREAGLE,
323 	VIDEOMODE_2XSAI,
324 	VIDEOMODE_SUPER2XSAI,
325 	VIDEOMODE_EPX,
326 	VIDEOMODE_HQ2X
327 };
328 
329 static int ErrorHandler (Display *, XErrorEvent *);
330 static bool8 CheckForPendingXEvents (Display *);
331 static void SetXRepeat (bool8);
332 static void SetupImage (void);
333 static void TakedownImage (void);
334 static void SetupXImage (void);
335 static void TakedownXImage (void);
336 #ifdef USE_XVIDEO
337 static void SetupXvImage (void);
338 static void TakedownXvImage (void);
339 #endif
340 static void Repaint (bool8);
341 static void Convert16To24 (int, int);
342 static void Convert16To24Packed (int, int);
343 
344 
S9xExtraDisplayUsage(void)345 void S9xExtraDisplayUsage (void)
346 {
347 	/*                               12345678901234567890123456789012345678901234567890123456789012345678901234567890 */
348 
349 	S9xMessage(S9X_INFO, S9X_USAGE, "-setrepeat                      Allow altering keyboard auto-repeat");
350 	S9xMessage(S9X_INFO, S9X_USAGE, "");
351 	S9xMessage(S9X_INFO, S9X_USAGE, "-fullscreen                     Switch to full-screen on start");
352 #ifdef USE_XVIDEO
353 	S9xMessage(S9X_INFO, S9X_USAGE, "-xvideo                         Hardware accelerated scaling");
354 	S9xMessage(S9X_INFO, S9X_USAGE, "-maxaspect                      Try to fill the display, in fullscreen");
355 #endif
356 	S9xMessage(S9X_INFO, S9X_USAGE, "");
357 	S9xMessage(S9X_INFO, S9X_USAGE, "-v1                             Video mode: Blocky (default)");
358 	S9xMessage(S9X_INFO, S9X_USAGE, "-v2                             Video mode: TV");
359 	S9xMessage(S9X_INFO, S9X_USAGE, "-v3                             Video mode: Smooth");
360 	S9xMessage(S9X_INFO, S9X_USAGE, "-v4                             Video mode: SuperEagle");
361 	S9xMessage(S9X_INFO, S9X_USAGE, "-v5                             Video mode: 2xSaI");
362 	S9xMessage(S9X_INFO, S9X_USAGE, "-v6                             Video mode: Super2xSaI");
363 	S9xMessage(S9X_INFO, S9X_USAGE, "-v7                             Video mode: EPX");
364 	S9xMessage(S9X_INFO, S9X_USAGE, "-v8                             Video mode: hq2x");
365 	S9xMessage(S9X_INFO, S9X_USAGE, "");
366 }
367 
S9xParseDisplayArg(char ** argv,int & i,int argc)368 void S9xParseDisplayArg (char **argv, int &i, int argc)
369 {
370 	if (!strcasecmp(argv[i], "-setrepeat"))
371 		GUI.no_repeat = FALSE;
372 	else
373 	if (!strcasecmp(argv[i], "-fullscreen"))
374 		GUI.fullscreen = TRUE;
375 	else
376 #ifdef USE_XVIDEO
377 	if (!strcasecmp(argv[i], "-xvideo"))
378 		GUI.use_xvideo = TRUE;
379 	else
380 	if (!strcasecmp(argv[i], "-maxaspect"))
381 		GUI.maxaspect = TRUE;
382 	else
383 #endif
384 	if (!strncasecmp(argv[i], "-v", 2))
385 	{
386 		switch (argv[i][2])
387 		{
388 			case '1':	GUI.video_mode = VIDEOMODE_BLOCKY;		break;
389 			case '2':	GUI.video_mode = VIDEOMODE_TV;			break;
390 			case '3':	GUI.video_mode = VIDEOMODE_SMOOTH;		break;
391 			case '4':	GUI.video_mode = VIDEOMODE_SUPEREAGLE;	break;
392 			case '5':	GUI.video_mode = VIDEOMODE_2XSAI;		break;
393 			case '6':	GUI.video_mode = VIDEOMODE_SUPER2XSAI;	break;
394 			case '7':	GUI.video_mode = VIDEOMODE_EPX;			break;
395 			case '8':	GUI.video_mode = VIDEOMODE_HQ2X;		break;
396 		}
397 	}
398 	else
399 		S9xUsage();
400 }
401 
S9xParseDisplayConfig(ConfigFile & conf,int pass)402 const char * S9xParseDisplayConfig (ConfigFile &conf, int pass)
403 {
404 	if (pass != 1)
405 		return ("Unix/X11");
406 
407 	if (!conf.GetBool("Unix::ClearAllControls", false))
408 	{
409 		keymaps.push_back(strpair_t("K00:k",            "Joypad1 Right"));
410 		keymaps.push_back(strpair_t("K00:Right",        "Joypad1 Right"));
411 		keymaps.push_back(strpair_t("K00:h",            "Joypad1 Left"));
412 		keymaps.push_back(strpair_t("K00:Left",         "Joypad1 Left"));
413 		keymaps.push_back(strpair_t("K00:j",            "Joypad1 Down"));
414 		keymaps.push_back(strpair_t("K00:n",            "Joypad1 Down"));
415 		keymaps.push_back(strpair_t("K00:Down",         "Joypad1 Down"));
416 		keymaps.push_back(strpair_t("K00:u",            "Joypad1 Up"));
417 		keymaps.push_back(strpair_t("K00:Up",           "Joypad1 Up"));
418 		keymaps.push_back(strpair_t("K00:Return",       "Joypad1 Start"));
419 		keymaps.push_back(strpair_t("K00:space",        "Joypad1 Select"));
420 		keymaps.push_back(strpair_t("K00:S+d",          "Joypad1 ToggleTurbo A"));
421 		keymaps.push_back(strpair_t("K00:C+d",          "Joypad1 ToggleSticky A"));
422 		keymaps.push_back(strpair_t("K00:d",            "Joypad1 A"));
423 		keymaps.push_back(strpair_t("K00:S+c",          "Joypad1 ToggleTurbo B"));
424 		keymaps.push_back(strpair_t("K00:C+c",          "Joypad1 ToggleSticky B"));
425 		keymaps.push_back(strpair_t("K00:c",            "Joypad1 B"));
426 		keymaps.push_back(strpair_t("K00:S+s",          "Joypad1 ToggleTurbo X"));
427 		keymaps.push_back(strpair_t("K00:C+s",          "Joypad1 ToggleSticky X"));
428 		keymaps.push_back(strpair_t("K00:s",            "Joypad1 X"));
429 		keymaps.push_back(strpair_t("K00:S+x",          "Joypad1 ToggleTurbo Y"));
430 		keymaps.push_back(strpair_t("K00:C+x",          "Joypad1 ToggleSticky Y"));
431 		keymaps.push_back(strpair_t("K00:x",            "Joypad1 Y"));
432 		keymaps.push_back(strpair_t("K00:S+a",          "Joypad1 ToggleTurbo L"));
433 		keymaps.push_back(strpair_t("K00:S+v",          "Joypad1 ToggleTurbo L"));
434 		keymaps.push_back(strpair_t("K00:C+a",          "Joypad1 ToggleSticky L"));
435 		keymaps.push_back(strpair_t("K00:C+v",          "Joypad1 ToggleSticky L"));
436 		keymaps.push_back(strpair_t("K00:a",            "Joypad1 L"));
437 		keymaps.push_back(strpair_t("K00:v",            "Joypad1 L"));
438 		keymaps.push_back(strpair_t("K00:S+z",          "Joypad1 ToggleTurbo R"));
439 		keymaps.push_back(strpair_t("K00:C+z",          "Joypad1 ToggleSticky R"));
440 		keymaps.push_back(strpair_t("K00:z",            "Joypad1 R"));
441 
442 		keymaps.push_back(strpair_t("K00:KP_Left",      "Joypad2 Left"));
443 		keymaps.push_back(strpair_t("K00:KP_Right",     "Joypad2 Right"));
444 		keymaps.push_back(strpair_t("K00:KP_Up",        "Joypad2 Up"));
445 		keymaps.push_back(strpair_t("K00:KP_Down",      "Joypad2 Down"));
446 		keymaps.push_back(strpair_t("K00:KP_Enter",     "Joypad2 Start"));
447 		keymaps.push_back(strpair_t("K00:KP_Add",       "Joypad2 Select"));
448 		keymaps.push_back(strpair_t("K00:Prior",        "Joypad2 A"));
449 		keymaps.push_back(strpair_t("K00:Next",         "Joypad2 B"));
450 		keymaps.push_back(strpair_t("K00:Home",         "Joypad2 X"));
451 		keymaps.push_back(strpair_t("K00:End",          "Joypad2 Y"));
452 		keymaps.push_back(strpair_t("K00:Insert",       "Joypad2 L"));
453 		keymaps.push_back(strpair_t("K00:Delete",       "Joypad2 R"));
454 
455 		keymaps.push_back(strpair_t("K00:A+F4",         "SoundChannel0"));
456 		keymaps.push_back(strpair_t("K00:C+F4",         "SoundChannel0"));
457 		keymaps.push_back(strpair_t("K00:A+F5",         "SoundChannel1"));
458 		keymaps.push_back(strpair_t("K00:C+F5",         "SoundChannel1"));
459 		keymaps.push_back(strpair_t("K00:A+F6",         "SoundChannel2"));
460 		keymaps.push_back(strpair_t("K00:C+F6",         "SoundChannel2"));
461 		keymaps.push_back(strpair_t("K00:A+F7",         "SoundChannel3"));
462 		keymaps.push_back(strpair_t("K00:C+F7",         "SoundChannel3"));
463 		keymaps.push_back(strpair_t("K00:A+F8",         "SoundChannel4"));
464 		keymaps.push_back(strpair_t("K00:C+F8",         "SoundChannel4"));
465 		keymaps.push_back(strpair_t("K00:A+F9",         "SoundChannel5"));
466 		keymaps.push_back(strpair_t("K00:C+F9",         "SoundChannel5"));
467 		keymaps.push_back(strpair_t("K00:A+F10",        "SoundChannel6"));
468 		keymaps.push_back(strpair_t("K00:C+F10",        "SoundChannel6"));
469 		keymaps.push_back(strpair_t("K00:A+F11",        "SoundChannel7"));
470 		keymaps.push_back(strpair_t("K00:C+F11",        "SoundChannel7"));
471 		keymaps.push_back(strpair_t("K00:A+F12",        "SoundChannelsOn"));
472 		keymaps.push_back(strpair_t("K00:C+F12",        "SoundChannelsOn"));
473 
474 		keymaps.push_back(strpair_t("K00:S+1",          "BeginRecordingMovie"));
475 		keymaps.push_back(strpair_t("K00:S+2",          "EndRecordingMovie"));
476 		keymaps.push_back(strpair_t("K00:S+3",          "LoadMovie"));
477 		keymaps.push_back(strpair_t("K00:A+F1",         "SaveSPC"));
478 		keymaps.push_back(strpair_t("K00:C+F1",         "SaveSPC"));
479 		keymaps.push_back(strpair_t("K00:F10",          "LoadOopsFile"));
480 		keymaps.push_back(strpair_t("K00:A+F2",         "LoadFreezeFile"));
481 		keymaps.push_back(strpair_t("K00:C+F2",         "LoadFreezeFile"));
482 		keymaps.push_back(strpair_t("K00:F11",          "LoadFreezeFile"));
483 		keymaps.push_back(strpair_t("K00:A+F3",         "SaveFreezeFile"));
484 		keymaps.push_back(strpair_t("K00:C+F3",         "SaveFreezeFile"));
485 		keymaps.push_back(strpair_t("K00:F12",          "SaveFreezeFile"));
486 		keymaps.push_back(strpair_t("K00:F1",           "QuickLoad000"));
487 		keymaps.push_back(strpair_t("K00:F2",           "QuickLoad001"));
488 		keymaps.push_back(strpair_t("K00:F3",           "QuickLoad002"));
489 		keymaps.push_back(strpair_t("K00:F4",           "QuickLoad003"));
490 		keymaps.push_back(strpair_t("K00:F5",           "QuickLoad004"));
491 		keymaps.push_back(strpair_t("K00:F6",           "QuickLoad005"));
492 		keymaps.push_back(strpair_t("K00:F7",           "QuickLoad006"));
493 		keymaps.push_back(strpair_t("K00:F8",           "QuickLoad007"));
494 		keymaps.push_back(strpair_t("K00:F9",           "QuickLoad008"));
495 		keymaps.push_back(strpair_t("K00:S+F1",         "QuickSave000"));
496 		keymaps.push_back(strpair_t("K00:S+F2",         "QuickSave001"));
497 		keymaps.push_back(strpair_t("K00:S+F3",         "QuickSave002"));
498 		keymaps.push_back(strpair_t("K00:S+F4",         "QuickSave003"));
499 		keymaps.push_back(strpair_t("K00:S+F5",         "QuickSave004"));
500 		keymaps.push_back(strpair_t("K00:S+F6",         "QuickSave005"));
501 		keymaps.push_back(strpair_t("K00:S+F7",         "QuickSave006"));
502 		keymaps.push_back(strpair_t("K00:S+F8",         "QuickSave007"));
503 		keymaps.push_back(strpair_t("K00:S+F9",         "QuickSave008"));
504 
505 		keymaps.push_back(strpair_t("K00:Scroll_Lock",  "Pause"));
506 		keymaps.push_back(strpair_t("K00:CS+Escape",    "Reset"));
507 		keymaps.push_back(strpair_t("K00:S+Escape",     "SoftReset"));
508 		keymaps.push_back(strpair_t("K00:Escape",       "ExitEmu"));
509 		keymaps.push_back(strpair_t("K00:Tab",          "EmuTurbo"));
510 		keymaps.push_back(strpair_t("K00:S+Tab",        "ToggleEmuTurbo"));
511 		keymaps.push_back(strpair_t("K00:A+equal",      "IncEmuTurbo"));
512 		keymaps.push_back(strpair_t("K00:A+minus",      "DecEmuTurbo"));
513 		keymaps.push_back(strpair_t("K00:C+equal",      "IncTurboSpeed"));
514 		keymaps.push_back(strpair_t("K00:C+minus",      "DecTurboSpeed"));
515 		keymaps.push_back(strpair_t("K00:equal",        "IncFrameRate"));
516 		keymaps.push_back(strpair_t("K00:minus",        "DecFrameRate"));
517 		keymaps.push_back(strpair_t("K00:S+equal",      "IncFrameTime"));
518 		keymaps.push_back(strpair_t("K00:S+minus",      "DecFrameTime"));
519 		keymaps.push_back(strpair_t("K00:6",            "SwapJoypads"));
520 		keymaps.push_back(strpair_t("K00:Print",        "Screenshot"));
521 
522 		keymaps.push_back(strpair_t("K00:1",            "ToggleBG0"));
523 		keymaps.push_back(strpair_t("K00:2",            "ToggleBG1"));
524 		keymaps.push_back(strpair_t("K00:3",            "ToggleBG2"));
525 		keymaps.push_back(strpair_t("K00:4",            "ToggleBG3"));
526 		keymaps.push_back(strpair_t("K00:5",            "ToggleSprites"));
527 		keymaps.push_back(strpair_t("K00:9",            "ToggleTransparency"));
528 		keymaps.push_back(strpair_t("K00:BackSpace",    "ClipWindows"));
529 		keymaps.push_back(strpair_t("K00:A+Escape",     "Debugger"));
530 
531 		keymaps.push_back(strpair_t("M00:B0",           "{Mouse1 L,Superscope Fire,Justifier1 Trigger}"));
532 		keymaps.push_back(strpair_t("M00:B1",           "{Justifier1 AimOffscreen Trigger,Superscope AimOffscreen}"));
533 		keymaps.push_back(strpair_t("M00:B2",           "{Mouse1 R,Superscope Cursor,Justifier1 Start}"));
534 		keymaps.push_back(strpair_t("M00:Pointer",      "Pointer Mouse1+Superscope+Justifier1"));
535 		keymaps.push_back(strpair_t("K00:grave",        "Superscope ToggleTurbo"));
536 		keymaps.push_back(strpair_t("K00:slash",        "Superscope Pause"));
537 
538 		keymaps.push_back(strpair_t("K00:r",            "Rewind"));
539 	}
540 
541 	GUI.no_repeat = !conf.GetBool("Unix/X11::SetKeyRepeat", TRUE);
542 	GUI.fullscreen = conf.GetBool("Unix/X11::Fullscreen", FALSE);
543 #ifdef USE_XVIDEO
544 	GUI.use_xvideo = conf.GetBool("Unix/X11::Xvideo", FALSE);
545 	GUI.maxaspect = conf.GetBool("Unix/X11::MaxAspect", FALSE);
546 #endif
547 
548 	if (conf.Exists("Unix/X11::VideoMode"))
549 	{
550 		GUI.video_mode = conf.GetUInt("Unix/X11::VideoMode", VIDEOMODE_BLOCKY);
551 		if (GUI.video_mode < 1 || GUI.video_mode > 8)
552 			GUI.video_mode = VIDEOMODE_BLOCKY;
553 	}
554 	else
555 		GUI.video_mode = VIDEOMODE_BLOCKY;
556 
557 	return ("Unix/X11");
558 }
559 
FatalError(const char * str)560 static void FatalError (const char *str)
561 {
562 	fprintf(stderr, "%s\n", str);
563 	S9xExit();
564 }
565 
ErrorHandler(Display * display,XErrorEvent * event)566 static int ErrorHandler (Display *display, XErrorEvent *event)
567 {
568 #ifdef MITSHM
569 	GUI.use_shared_memory = FALSE;
570 #endif
571 	return (0);
572 }
573 
574 #ifdef USE_XVIDEO
get_inv_shift(uint32 mask,int bpp)575 static int get_inv_shift (uint32 mask, int bpp)
576 {
577     int i;
578 
579     // Find mask
580     for (i = 0; (i < bpp) && !(mask & (1 << i)); i++) {};
581 
582     // Find start of mask
583     for (; (i < bpp) && (mask & (1 << i)); i++) {};
584 
585     return (bpp - i);
586 }
587 
CLAMP(int v,int min,int max)588 static unsigned char CLAMP (int v, int min, int max)
589 {
590 	if (v < min) return min;
591 	if (v > max) return max;
592 	return v;
593 }
594 
SetupXvideo()595 static bool8 SetupXvideo()
596 {
597 	int ret;
598 
599 	// Init xv_port
600 	GUI.xv_port = -1;
601 
602 	/////////////////////
603 	// Check that Xvideo extension seems OK
604 	unsigned int p_version, p_release, p_request_base, p_event_base, p_error_base;
605 	ret = XvQueryExtension(GUI.display,
606 		&p_version, &p_release, &p_request_base,
607 		&p_event_base, &p_error_base);
608 	if (ret != Success) { fprintf(stderr,"XvQueryExtension error\n"); return FALSE; }
609 	printf("XvExtension version %i.%i\n",p_version,p_release);
610 
611 	/////////////////////
612 	// Get info about the Adaptors available for this window
613 	unsigned int	p_num_adaptors;
614 	XvAdaptorInfo*	ai;
615 	ret = XvQueryAdaptors(GUI.display, GUI.window, &p_num_adaptors, &ai);
616 
617 	if (ret != Success || p_num_adaptors == 0) {
618 		fprintf(stderr,"XvQueryAdaptors error.");
619 		return FALSE;
620 	}
621 	printf("XvQueryAdaptors: %d adaptor(s) found.\n",p_num_adaptors);
622 
623 	unsigned int minAdaptor = 0, maxAdaptor = p_num_adaptors;
624 	// Allow user to force adaptor choice
625 	/* if (adaptor >= 0 && adaptor < p_num_adaptors)
626 	{
627 		if (verbose) std::cout << "Forcing adaptor " << adaptor << ", '" << ai[adaptor].name << "'" << std::endl;
628 		minAdaptor = adaptor;
629 		maxAdaptor = adaptor + 1;
630 	} */
631 
632 	/////////////////////
633 	// Iterate through list of available adaptors.
634 	//  Grab a port if we can.
635 	for (unsigned int i = minAdaptor; i < maxAdaptor && GUI.xv_port < 0; i++)
636 	{
637 		// We need to find one supporting XvInputMask and XvImageMask.
638 		if (! (ai[i].type & XvImageMask)) continue;
639 		if (! (ai[i].type & XvInputMask)) continue;
640 
641 		printf("\tAdaptor #%d: [%s]: %ld port(s) available.\n", i, ai[i].name, ai[i].num_ports);
642 
643 		// Get encodings available here
644 		//  AFAIK all ports on an adapter share the same encodings info.
645 		unsigned int encodings;
646 		XvEncodingInfo	*ei;
647 		ret = XvQueryEncodings(GUI.display, ai[i].base_id, &encodings, &ei);
648 		if (ret != Success || encodings == 0) {
649 	                fprintf(stderr,"XvQueryEncodings error.");
650 			continue;
651 		}
652 
653 		// Ensure the XV_IMAGE encoding available has sufficient width/height for us.
654 		bool8 can_fit = FALSE;
655 		for (unsigned int j = 0; j < encodings; j++)
656 		{
657 			if (strcmp(ei[j].name,"XV_IMAGE")) continue;
658 			if (ei[j].width >= SNES_WIDTH * 2 &&
659 				ei[j].height >= SNES_HEIGHT_EXTENDED * 2)
660 			{
661 				can_fit = TRUE;
662 				break;
663 			}
664 		}
665 		XvFreeEncodingInfo(ei);
666 
667 		if (can_fit == FALSE)
668 		{
669 			fprintf(stderr,"\tDid not find XV_IMAGE encoding with enough max size\n");
670 			continue;
671 		}
672 
673 		// Phew. If we've made it this far, we can try to choose it for our output port.
674 		for (unsigned int p = ai[i].base_id; p < ai[i].base_id+ai[i].num_ports; p++)
675 		{
676 			ret = XvGrabPort(GUI.display, p, CurrentTime);
677 			if (ret == Success)
678 			{
679 				printf("\tSuccessfully bound to Xv port %d\n",p);
680 				GUI.xv_port = p;
681 				break;
682 			} else {
683 				fprintf(stderr,"\tXvGrabPort port %d fail.\n",p);
684 			}
685 		}
686 	}
687 	XvFreeAdaptorInfo(ai);
688 
689 	/////////////////////
690 	// Bail out here if we haven't managed to bind to any port.
691 	if (GUI.xv_port < 0)
692 	{
693 		fprintf(stderr,"No suitable xv_port found in any Adaptors.\n");
694 		return FALSE;
695 	}
696 
697 	// Xv ports can have Attributes (hue, saturation, etc)
698 	/* Set XV_AUTOPAINT_COLORKEY _only_ if available */
699 	int num_attrs;
700 	XvAttribute*	port_attr;
701 	port_attr = XvQueryPortAttributes (GUI.display, GUI.xv_port, &num_attrs);
702 
703 	for (int i = 0; i < num_attrs; i++)
704 	{
705 		if (!strcmp (port_attr[i].name, "XV_AUTOPAINT_COLORKEY"))
706 		{
707 			Atom colorkey;
708 
709 			colorkey = XInternAtom (GUI.display, "XV_AUTOPAINT_COLORKEY", True);
710 			if (colorkey != None)
711 			{
712 				XvSetPortAttribute (GUI.display, GUI.xv_port, colorkey, 1);
713 				printf("\tSet XV_AUTOPAINT_COLORKEY.\n");
714 			}
715 		}
716 	}
717 	XFree(port_attr);
718 
719 	// Now we need to find to find the image format to use for output.
720 	//  There are two steps to this:
721 	//    Prefer an XvRGB version of lowest bitdepth.
722 	//  If that's not available use YUY2
723 	int	formats;
724 	XvImageFormatValues*	fo;
725 	fo = XvListImageFormats(GUI.display, GUI.xv_port, &formats);
726 	if (formats == 0)
727 	{
728 		fprintf(stderr,"No valid image formats for Xv port!");
729 		return FALSE;
730 	}
731 
732 	/* Ok time to search for a good Format */
733 	GUI.xv_format = FOURCC_YUY2;
734 	GUI.xv_bpp = 0x7FFFFFFF;
735 
736 	for (int i = 0; i < formats; i++)
737 	{
738 		if (fo[i].id == 0x3 || fo[i].type == XvRGB)
739 		{
740 			if (fo[i].bits_per_pixel < GUI.xv_bpp)
741 			{
742 				GUI.xv_format = fo[i].id;
743 				GUI.xv_bpp = fo[i].bits_per_pixel;
744 				GUI.bytes_per_pixel = (GUI.xv_bpp == 15) ? 2 : GUI.xv_bpp >> 3;
745 				GUI.depth = fo[i].depth;
746 
747 				GUI.red_shift = get_inv_shift (fo[i].red_mask, GUI.xv_bpp);
748 				GUI.green_shift = get_inv_shift (fo[i].green_mask, GUI.xv_bpp);
749 				GUI.blue_shift = get_inv_shift (fo[i].blue_mask, GUI.xv_bpp);
750 
751 				/* Check for red-blue inversion on SiliconMotion drivers */
752 				if (fo[i].red_mask  == 0x001f &&
753 					fo[i].blue_mask == 0x7c00)
754 				{
755 					int copy = GUI.red_shift;
756 					GUI.red_shift = GUI.blue_shift;
757 					GUI.blue_shift = copy;
758 				}
759 
760 				/* on big-endian Xv still seems to like LSB order */
761 				/*if (config->force_inverted_byte_order)
762 					S9xSetEndianess (ENDIAN_MSB);
763 				else
764 					S9xSetEndianess (ENDIAN_LSB); */
765 			}
766 		}
767 	}
768 	free (fo);
769 
770 	if (GUI.xv_format != FOURCC_YUY2)
771 	{
772 		printf("Selected XvRGB format: %d bpp\n",GUI.xv_bpp);
773 	} else {
774 		// use YUY2
775 		printf("Fallback to YUY2 format.\n");
776 		GUI.depth = 15;
777 
778 		/* Build a table for yuv conversion */
779 		for (unsigned int color = 0; color < (1 << 15); color++)
780 		{
781 			int r, g, b;
782 			int y, u, v;
783 
784 			r = (color & 0x7c00) >> 7;
785 			g = (color & 0x03e0) >> 2;
786 			b = (color & 0x001F) << 3;
787 
788 			y = (int) ((0.257  * ((double) r)) + (0.504  * ((double) g)) + (0.098  * ((double) b)) + 16.0);
789 			u = (int) ((-0.148 * ((double) r)) + (-0.291 * ((double) g)) + (0.439  * ((double) b)) + 128.0);
790 			v = (int) ((0.439  * ((double) r)) + (-0.368 * ((double) g)) + (-0.071 * ((double) b)) + 128.0);
791 
792 			GUI.y_table[color] = CLAMP (y, 0, 255);
793 			GUI.u_table[color] = CLAMP (u, 0, 255);
794 			GUI.v_table[color] = CLAMP (v, 0, 255);
795 		}
796 	}
797 
798 	return TRUE;
799 }
800 #endif
801 
S9xInitDisplay(int argc,char ** argv)802 void S9xInitDisplay (int argc, char **argv)
803 {
804 	GUI.display = XOpenDisplay(NULL);
805 	if (GUI.display == NULL)
806 		FatalError("Failed to connect to X server.");
807 
808 	GUI.screen     = DefaultScreenOfDisplay(GUI.display);
809 	GUI.screen_num = XScreenNumberOfScreen(GUI.screen);
810 	GUI.visual     = DefaultVisualOfScreen(GUI.screen);
811 
812 	XVisualInfo	plate, *matches;
813 	int			count;
814 
815 	plate.visualid = XVisualIDFromVisual(GUI.visual);
816 	matches = XGetVisualInfo(GUI.display, VisualIDMask, &plate, &count);
817 	if (!count)
818 		FatalError("Your X Window System server is unwell!");
819 
820 	GUI.depth = matches[0].depth;
821 	if ((GUI.depth != 15 && GUI.depth != 16 && GUI.depth != 24) || (matches[0].c_class != TrueColor))
822 		FatalError("Requiers 15, 16, 24 or 32-bit color depth supporting TrueColor.");
823 
824 	GUI.red_shift   = ffs(matches[0].red_mask)   - 1;
825 	GUI.green_shift = ffs(matches[0].green_mask) - 1;
826 	GUI.blue_shift  = ffs(matches[0].blue_mask)  - 1;
827 	GUI.red_size    = matches[0].red_mask   >> GUI.red_shift;
828 	GUI.green_size  = matches[0].green_mask >> GUI.green_shift;
829 	GUI.blue_size   = matches[0].blue_mask  >> GUI.blue_shift;
830 	if (GUI.depth == 16 && GUI.green_size == 63)
831 		GUI.green_shift++;
832 
833 	XFree(matches);
834 
835 	// Init various scale-filters
836 	S9xBlitFilterInit();
837 	S9xBlit2xSaIFilterInit();
838 	S9xBlitHQ2xFilterInit();
839 
840 	/* Set up parameters for creating the window */
841 	XSetWindowAttributes	attrib;
842 
843 	memset(&attrib, 0, sizeof(attrib));
844 	attrib.background_pixel = BlackPixelOfScreen(GUI.screen);
845 	attrib.colormap = XCreateColormap(GUI.display, RootWindowOfScreen(GUI.screen), GUI.visual, AllocNone);
846 
847 	/* Try to switch to Fullscreen. */
848 	if (GUI.fullscreen == TRUE)
849 	{
850 		/* Create the window with maximum screen width,height positioned at 0,0. */
851 		GUI.window = XCreateWindow(GUI.display, RootWindowOfScreen(GUI.screen),
852 							0, 0,
853 							WidthOfScreen(GUI.screen), HeightOfScreen(GUI.screen), 0,
854 							GUI.depth, InputOutput, GUI.visual, CWBackPixel | CWColormap, &attrib);
855 
856 #ifdef USE_XVIDEO
857 		if (GUI.use_xvideo)
858 		{
859 			// Set some defaults
860 			GUI.scale_w = WidthOfScreen(GUI.screen);
861 			GUI.scale_h = HeightOfScreen(GUI.screen);
862 
863 			GUI.imageHeight = SNES_HEIGHT_EXTENDED * 2;
864 
865 			if (! GUI.maxaspect)
866 			{
867 				// Compute the maximum screen size for scaling xvideo window.
868 				double screenAspect = (double)WidthOfScreen(GUI.screen) / HeightOfScreen(GUI.screen);
869 				double snesAspect = (double)SNES_WIDTH / SNES_HEIGHT_EXTENDED;
870 				double ratio = screenAspect / snesAspect;
871 
872 				printf("\tScreen (%dx%d) aspect %f vs SNES (%dx%d) aspect %f (ratio: %f)\n",
873 					WidthOfScreen(GUI.screen),HeightOfScreen(GUI.screen),screenAspect,
874 					SNES_WIDTH,SNES_HEIGHT_EXTENDED,snesAspect,
875 					ratio);
876 
877 				// Correct aspect ratio
878 				if (screenAspect > snesAspect)
879 				{
880 					// widescreen monitor, 4:3 snes
881 					//  match height, scale width
882 					GUI.scale_w /= ratio;
883 					GUI.x_offset = (WidthOfScreen(GUI.screen) - GUI.scale_w) / 2;
884 				} else {
885 					// narrow monitor, 4:3 snes
886 					//  match width, scale height
887 					GUI.scale_h *= ratio;
888 					GUI.y_offset = (HeightOfScreen(GUI.screen) - GUI.scale_h) / 2;
889 				}
890 			}
891 
892 			printf("\tUsing size %dx%d with offset (%d,%d)\n",GUI.scale_w,GUI.scale_h,GUI.x_offset,GUI.y_offset);
893 		}
894 		else
895 #endif
896 		{
897 			/* Last: position the output window in the center of the screen. */
898 			GUI.x_offset = (WidthOfScreen(GUI.screen) - SNES_WIDTH * 2) / 2;
899 			GUI.y_offset = (HeightOfScreen(GUI.screen) - SNES_HEIGHT_EXTENDED * 2) / 2;
900 		}
901 	} else {
902 		/* Create the window. */
903 		GUI.window = XCreateWindow(GUI.display, RootWindowOfScreen(GUI.screen),
904 								   (WidthOfScreen(GUI.screen) - SNES_WIDTH * 2) / 2, (HeightOfScreen(GUI.screen) - SNES_HEIGHT_EXTENDED * 2) / 2,
905 								   SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED * 2, 0, GUI.depth, InputOutput, GUI.visual, CWBackPixel | CWColormap, &attrib);
906 
907 		/* Tell the Window Manager that we do not wish to be resizable */
908 		XSizeHints      Hints;
909 		memset((void *) &Hints, 0, sizeof(XSizeHints));
910 
911 		Hints.flags      = PSize | PMinSize | PMaxSize;
912 		Hints.min_width  = Hints.max_width  = Hints.base_width  = SNES_WIDTH * 2;
913 		Hints.min_height = Hints.max_height = Hints.base_height = SNES_HEIGHT_EXTENDED * 2;
914 		XSetWMNormalHints(GUI.display, GUI.window, &Hints);
915 
916 		/* Last: Windowed SNES is not drawn with any offsets. */
917 		GUI.x_offset = GUI.y_offset = 0;
918 #ifdef USE_XVIDEO
919 		GUI.scale_w = SNES_WIDTH * 2;
920 		GUI.scale_h = SNES_HEIGHT_EXTENDED * 2;
921 #endif
922 	}
923 
924 	/* Load UI cursors */
925 	static XColor	bg, fg;
926 	static char		data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
927 	Pixmap			bitmap;
928 
929 	bitmap = XCreateBitmapFromData(GUI.display, GUI.window, data, 8, 8);
930 	GUI.point_cursor = XCreatePixmapCursor(GUI.display, bitmap, bitmap, &fg, &bg, 0, 0);
931 	XDefineCursor(GUI.display, GUI.window, GUI.point_cursor);
932 	GUI.cross_hair_cursor = XCreateFontCursor(GUI.display, XC_crosshair);
933 
934 	GUI.gc = DefaultGCOfScreen(GUI.screen);
935 
936 	/* Other window-manager hints */
937 	XWMHints	WMHints;
938 
939 	memset((void *) &WMHints, 0, sizeof(XWMHints));
940 
941 	/* Rely on the Window Manager to provide us with keyboard input */
942 	WMHints.input    = True;
943 	WMHints.flags    = InputHint;
944 
945 	XSetWMHints(GUI.display, GUI.window, &WMHints);
946 	XSelectInput(GUI.display, GUI.window, FocusChangeMask | ExposureMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask | ButtonPressMask | ButtonReleaseMask);
947 
948 	/* Bring up our window (and put it in foreground) */
949 	XMapRaised(GUI.display, GUI.window);
950 	XClearWindow(GUI.display, GUI.window);
951 
952 	// Wait for map
953 	XEvent event;
954 	do {
955 		XNextEvent(GUI.display, &event);
956 	} while (event.type != MapNotify || event.xmap.event != GUI.window);
957 
958 	if (GUI.fullscreen)
959 	{
960 		/* Try to tell the Window Manager not to decorate this window. */
961 		Atom wm_state   = XInternAtom (GUI.display, "_NET_WM_STATE", true );
962 		Atom wm_fullscreen = XInternAtom (GUI.display, "_NET_WM_STATE_FULLSCREEN", true );
963 
964 		XChangeProperty(GUI.display, GUI.window, wm_state, XA_ATOM, 32, PropModeReplace, (unsigned char *)&wm_fullscreen, 1);
965 	}
966 
967 #ifdef USE_XVIDEO
968 	if (GUI.use_xvideo)
969 	{
970 		GUI.use_xvideo = SetupXvideo();
971 	}
972 #endif
973 
974 	switch (GUI.depth)
975 	{
976 		default:
977 		case 32:
978 		case 24:
979 			S9xSetRenderPixelFormat(RGB555);
980 			GUI.pixel_format = 555;
981 			break;
982 
983 		case 16:
984 			if (GUI.red_size != GUI.green_size || GUI.blue_size != GUI.green_size)
985 			{
986 				// 565 format
987 				if (GUI.green_shift > GUI.blue_shift && GUI.green_shift > GUI.red_shift)
988 					S9xSetRenderPixelFormat(GBR565);
989 				else
990 				if (GUI.red_shift > GUI.blue_shift)
991 					S9xSetRenderPixelFormat(RGB565);
992 				else
993 					S9xSetRenderPixelFormat(BGR565);
994 
995 				GUI.pixel_format = 565;
996 				break;
997 			}
998 			// FALL ...
999 		case 15:
1000 			if (GUI.green_shift > GUI.blue_shift && GUI.green_shift > GUI.red_shift)
1001 				S9xSetRenderPixelFormat(GBR555);
1002 			else
1003 			if (GUI.red_shift > GUI.blue_shift)
1004 				S9xSetRenderPixelFormat(RGB555);
1005 			else
1006 				S9xSetRenderPixelFormat(BGR555);
1007 
1008 			GUI.pixel_format = 555;
1009 			break;
1010 	}
1011 
1012 	SetupImage();
1013 
1014 	switch (GUI.depth)
1015 	{
1016 		default:
1017 		case 32:
1018 			GUI.bytes_per_pixel = 4;
1019 			break;
1020 
1021 		case 24:
1022 			if (GUI.image->bits_per_pixel == 24)
1023 				GUI.bytes_per_pixel = 3;
1024 			else
1025 				GUI.bytes_per_pixel = 4;
1026 			break;
1027 
1028 		case 15:
1029 		case 16:
1030 			GUI.bytes_per_pixel = 2;
1031 			break;
1032 	}
1033 
1034 	printf("Using internal pixel format %d\n",GUI.pixel_format);
1035 }
1036 
S9xDeinitDisplay(void)1037 void S9xDeinitDisplay (void)
1038 {
1039 	TakedownImage();
1040 	if (GUI.display != NULL)
1041 	{
1042 #ifdef USE_XVIDEO
1043 		if (GUI.use_xvideo)
1044 		{
1045 			XvUngrabPort(GUI.display,GUI.xv_port,CurrentTime);
1046 		}
1047 #endif
1048 		S9xTextMode();
1049 		XSync(GUI.display, False);
1050 		XCloseDisplay(GUI.display);
1051 	}
1052 	S9xBlitFilterDeinit();
1053 	S9xBlit2xSaIFilterDeinit();
1054 	S9xBlitHQ2xFilterDeinit();
1055 }
1056 
SetupImage(void)1057 static void SetupImage (void)
1058 {
1059 	TakedownImage();
1060 
1061 	// Create new image struct
1062 	GUI.image = (Image *) calloc(sizeof(Image), 1);
1063 
1064 #ifdef USE_XVIDEO
1065 	if (GUI.use_xvideo)
1066 		SetupXvImage();
1067 	if (!GUI.use_xvideo)
1068 #endif
1069 		SetupXImage();
1070 
1071 	// Setup SNES buffers
1072 	GFX.Pitch = SNES_WIDTH * 2 * 2;
1073 	GUI.snes_buffer = (uint8 *) calloc(GFX.Pitch * ((SNES_HEIGHT_EXTENDED + 4) * 2), 1);
1074 	if (!GUI.snes_buffer)
1075 		FatalError("Failed to allocate GUI.snes_buffer.");
1076 
1077 	GFX.Screen = (uint16 *) (GUI.snes_buffer + (GFX.Pitch * 2 * 2));
1078 
1079 	GUI.filter_buffer = (uint8 *) calloc((SNES_WIDTH * 2) * 2 * (SNES_HEIGHT_EXTENDED * 2), 1);
1080 	if (!GUI.filter_buffer)
1081 		FatalError("Failed to allocate GUI.filter_buffer.");
1082 
1083 #ifdef USE_XVIDEO
1084 	if ((GUI.depth == 15 || GUI.depth == 16) && GUI.xv_format != FOURCC_YUY2)
1085 #else
1086 	if (GUI.depth == 15 || GUI.depth == 16)
1087 #endif
1088 	{
1089 		GUI.blit_screen_pitch = GUI.image->bytes_per_line;
1090 		GUI.blit_screen       = (uint8 *) GUI.image->data;
1091 		GUI.need_convert      = FALSE;
1092 	}
1093 	else
1094 	{
1095 		GUI.blit_screen_pitch = (SNES_WIDTH * 2) * 2;
1096 		GUI.blit_screen       = GUI.filter_buffer;
1097 		GUI.need_convert      = TRUE;
1098 	}
1099 	if (GUI.need_convert) { printf("\tImage conversion needed before blit.\n"); }
1100 
1101 	S9xGraphicsInit();
1102 }
1103 
TakedownImage(void)1104 static void TakedownImage (void)
1105 {
1106 	if (GUI.snes_buffer)
1107 	{
1108 		free(GUI.snes_buffer);
1109 		GUI.snes_buffer = NULL;
1110 	}
1111 
1112 	if (GUI.filter_buffer)
1113 	{
1114 		free(GUI.filter_buffer);
1115 		GUI.filter_buffer = NULL;
1116 	}
1117 
1118 	if (GUI.image)
1119 	{
1120 #ifdef USE_XVIDEO
1121 		if (GUI.use_xvideo)
1122 			TakedownXvImage();
1123 		else
1124 #endif
1125 			TakedownXImage();
1126 
1127 		free(GUI.image);
1128 		GUI.image = NULL;
1129 	}
1130 
1131 	S9xGraphicsDeinit();
1132 }
1133 
SetupXImage(void)1134 static void SetupXImage (void)
1135 {
1136 #ifdef MITSHM
1137 	GUI.use_shared_memory = TRUE;
1138 
1139 	int		major, minor;
1140 	Bool	shared;
1141 
1142 	if (!XShmQueryVersion(GUI.display, &major, &minor, &shared) || !shared)
1143 		GUI.image->ximage = NULL;
1144 	else
1145 		GUI.image->ximage = XShmCreateImage(GUI.display, GUI.visual, GUI.depth, ZPixmap, NULL, &GUI.sm_info, SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED * 2);
1146 
1147 	if (!GUI.image->ximage)
1148 		GUI.use_shared_memory = FALSE;
1149 	else
1150 	{
1151 		// set main Image struct vars
1152 		GUI.image->height = GUI.image->ximage->height;
1153 		GUI.image->bytes_per_line = GUI.image->ximage->bytes_per_line;
1154 		GUI.image->data_size = GUI.image->bytes_per_line * GUI.image->height;
1155 
1156 		GUI.sm_info.shmid = shmget(IPC_PRIVATE, GUI.image->data_size, IPC_CREAT | 0777);
1157 		if (GUI.sm_info.shmid < 0)
1158 		{
1159 			XDestroyImage(GUI.image->ximage);
1160 			GUI.use_shared_memory = FALSE;
1161 		}
1162 		else
1163 		{
1164 			GUI.image->ximage->data = GUI.sm_info.shmaddr = (char *) shmat(GUI.sm_info.shmid, 0, 0);
1165 			if (!GUI.image->ximage->data)
1166 			{
1167 				XDestroyImage(GUI.image->ximage);
1168 				shmctl(GUI.sm_info.shmid, IPC_RMID, 0);
1169 				GUI.use_shared_memory = FALSE;
1170 			}
1171 			else
1172 			{
1173 				GUI.sm_info.readOnly = False;
1174 
1175 				XSetErrorHandler(ErrorHandler);
1176 				XShmAttach(GUI.display, &GUI.sm_info);
1177 				XSync(GUI.display, False);
1178 
1179 				// X Error handler might clear GUI.use_shared_memory if XShmAttach failed.
1180 				if (!GUI.use_shared_memory)
1181 				{
1182 					XDestroyImage(GUI.image->ximage);
1183 					shmdt(GUI.sm_info.shmaddr);
1184 					shmctl(GUI.sm_info.shmid, IPC_RMID, 0);
1185 				} else
1186 					printf("Created XShmImage, size %d\n",GUI.image->data_size);
1187 			}
1188 		}
1189 	}
1190 
1191 	if (!GUI.use_shared_memory)
1192 	{
1193 		fprintf(stderr, "use_shared_memory failed, switching to XPutImage.\n");
1194 #endif
1195 		GUI.image->ximage = XCreateImage(GUI.display, GUI.visual, GUI.depth, ZPixmap, 0, NULL, SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED * 2, BitmapUnit(GUI.display), 0);
1196 		// set main Image struct vars
1197 		GUI.image->height = GUI.image->ximage->height;
1198 		GUI.image->bytes_per_line = GUI.image->ximage->bytes_per_line;
1199 		GUI.image->data_size = GUI.image->bytes_per_line * GUI.image->height;
1200 
1201 		GUI.image->ximage->data = (char *) malloc(GUI.image->data_size);
1202 		if (!GUI.image->ximage || !GUI.image->ximage->data)
1203 			FatalError("XCreateImage failed.");
1204 		printf("Created XImage, size %d\n",GUI.image->data_size);
1205 #ifdef MITSHM
1206 	}
1207 #endif
1208 
1209 	// Set final values
1210 	GUI.image->bits_per_pixel = GUI.image->ximage->bits_per_pixel;
1211 	GUI.image->data = GUI.image->ximage->data;
1212 
1213 #ifdef LSB_FIRST
1214 	GUI.image->ximage->byte_order = LSBFirst;
1215 #else
1216 	GUI.image->ximage->byte_order = MSBFirst;
1217 #endif
1218 }
1219 
TakedownXImage(void)1220 static void TakedownXImage (void)
1221 {
1222 	if (GUI.image->ximage)
1223 	{
1224 	#ifdef MITSHM
1225 		if (GUI.use_shared_memory)
1226 		{
1227 			XShmDetach(GUI.display, &GUI.sm_info);
1228 			GUI.image->ximage->data = NULL;
1229 			XDestroyImage(GUI.image->ximage);
1230 			if (GUI.sm_info.shmaddr)
1231 				shmdt(GUI.sm_info.shmaddr);
1232 			if (GUI.sm_info.shmid >= 0)
1233 				shmctl(GUI.sm_info.shmid, IPC_RMID, 0);
1234 			GUI.image->ximage = NULL;
1235 		}
1236 		else
1237 	#endif
1238 		{
1239 			XDestroyImage(GUI.image->ximage);
1240 			GUI.image->ximage = NULL;
1241 		}
1242 	}
1243 }
1244 
1245 #ifdef USE_XVIDEO
SetupXvImage(void)1246 static void SetupXvImage (void)
1247 {
1248 #ifdef MITSHM
1249 	GUI.use_shared_memory = TRUE;
1250 
1251 	int		major, minor;
1252 	Bool	shared;
1253 
1254 	if (!XShmQueryVersion(GUI.display, &major, &minor, &shared) || !shared)
1255 		GUI.image->xvimage = NULL;
1256 	else
1257 		GUI.image->xvimage = XvShmCreateImage(GUI.display, GUI.xv_port, GUI.xv_format, NULL, SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED * 2, &GUI.sm_info);
1258 
1259 	if (!GUI.image->xvimage)
1260 		GUI.use_shared_memory = FALSE;
1261 	else
1262 	{
1263 		GUI.image->height = SNES_HEIGHT_EXTENDED * 2;
1264 		GUI.image->data_size = GUI.image->xvimage->data_size;
1265 		GUI.image->bytes_per_line = GUI.image->data_size / GUI.image->height;
1266 		GUI.sm_info.shmid = shmget(IPC_PRIVATE, GUI.image->data_size, IPC_CREAT | 0777);
1267 		if (GUI.sm_info.shmid < 0)
1268 		{
1269 			XFree(GUI.image->xvimage);
1270 			GUI.use_shared_memory = FALSE;
1271 		}
1272 		else
1273 		{
1274 			GUI.image->xvimage->data = GUI.sm_info.shmaddr = (char *) shmat(GUI.sm_info.shmid, 0, 0);
1275 			if (!GUI.image->xvimage->data)
1276 			{
1277 				XFree(GUI.image->xvimage);
1278 				shmctl(GUI.sm_info.shmid, IPC_RMID, 0);
1279 				GUI.use_shared_memory = FALSE;
1280 			}
1281 			else
1282 			{
1283 				GUI.sm_info.readOnly = False;
1284 
1285 				XSetErrorHandler(ErrorHandler);
1286 				XShmAttach(GUI.display, &GUI.sm_info);
1287 				XSync(GUI.display, False);
1288 
1289 				// X Error handler might clear GUI.use_shared_memory if XShmAttach failed.
1290 				if (!GUI.use_shared_memory)
1291 				{
1292 					XFree(GUI.image->xvimage);
1293 					shmdt(GUI.sm_info.shmaddr);
1294 					shmctl(GUI.sm_info.shmid, IPC_RMID, 0);
1295 				} else
1296 					printf("Created XvShmImage, size %d\n",GUI.image->data_size);
1297 			}
1298 		}
1299 	}
1300 
1301 	if (!GUI.use_shared_memory)
1302 	{
1303 		fprintf(stderr, "use_shared_memory failed, switching to XvPutImage.\n");
1304 #endif
1305 		GUI.image->xvimage = XvCreateImage(GUI.display, GUI.xv_port, GUI.xv_format, NULL, SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED * 2);
1306 		GUI.image->height = SNES_HEIGHT_EXTENDED * 2;
1307 		GUI.image->data_size = GUI.image->xvimage->data_size;
1308 		GUI.image->bytes_per_line = GUI.image->data_size / GUI.image->height;
1309 
1310 		GUI.image->xvimage->data = (char *) malloc(GUI.image->data_size);
1311 		if (!GUI.image->xvimage || !GUI.image->xvimage->data)
1312 		{
1313 			fprintf(stderr, "XvCreateImage failed, falling back to software blit.\n");
1314 			GUI.use_xvideo = FALSE;
1315 			return;
1316 		}
1317 		printf("Created XvImage, size %d\n",GUI.image->data_size);
1318 #ifdef MITSHM
1319 	}
1320 #endif
1321 	// Set final values
1322 	GUI.image->bits_per_pixel = GUI.xv_bpp;
1323 	GUI.image->data = GUI.image->xvimage->data;
1324 }
1325 
TakedownXvImage(void)1326 static void TakedownXvImage (void)
1327 {
1328 	if (GUI.image->xvimage)
1329 	{
1330 	#ifdef MITSHM
1331 		if (GUI.use_shared_memory)
1332 		{
1333 			XShmDetach(GUI.display, &GUI.sm_info);
1334 			GUI.image->xvimage->data = NULL;
1335 			XFree(GUI.image->xvimage);
1336 			if (GUI.sm_info.shmaddr)
1337 				shmdt(GUI.sm_info.shmaddr);
1338 			if (GUI.sm_info.shmid >= 0)
1339 				shmctl(GUI.sm_info.shmid, IPC_RMID, 0);
1340 			GUI.image->xvimage = NULL;
1341 		}
1342 		else
1343 	#endif
1344 		{
1345 			free(GUI.image->xvimage->data);
1346 			//GUI.image->xvimage->data = NULL;
1347 			XFree(GUI.image->xvimage);
1348 			GUI.image->xvimage = NULL;
1349 		}
1350 	}
1351 }
1352 #endif
1353 
S9xPutImage(int width,int height)1354 void S9xPutImage (int width, int height)
1355 {
1356 	static int	prevWidth = 0, prevHeight = 0;
1357 	int			copyWidth, copyHeight;
1358 	Blitter		blitFn = NULL;
1359 
1360 	if (GUI.video_mode == VIDEOMODE_BLOCKY || GUI.video_mode == VIDEOMODE_TV || GUI.video_mode == VIDEOMODE_SMOOTH)
1361 		if ((width <= SNES_WIDTH) && ((prevWidth != width) || (prevHeight != height)))
1362 			S9xBlitClearDelta();
1363 
1364 	if (width <= SNES_WIDTH)
1365 	{
1366 		if (height > SNES_HEIGHT_EXTENDED)
1367 		{
1368 			copyWidth  = width * 2;
1369 			copyHeight = height;
1370 			blitFn = S9xBlitPixSimple2x1;
1371 		}
1372 		else
1373 		{
1374 			copyWidth  = width  * 2;
1375 			copyHeight = height * 2;
1376 
1377 			switch (GUI.video_mode)
1378 			{
1379 				case VIDEOMODE_BLOCKY:		blitFn = S9xBlitPixSimple2x2;		break;
1380 				case VIDEOMODE_TV:			blitFn = S9xBlitPixTV2x2;			break;
1381 				case VIDEOMODE_SMOOTH:		blitFn = S9xBlitPixSmooth2x2;		break;
1382 				case VIDEOMODE_SUPEREAGLE:	blitFn = S9xBlitPixSuperEagle16;	break;
1383 				case VIDEOMODE_2XSAI:		blitFn = S9xBlitPix2xSaI16;			break;
1384 				case VIDEOMODE_SUPER2XSAI:	blitFn = S9xBlitPixSuper2xSaI16;	break;
1385 				case VIDEOMODE_EPX:			blitFn = S9xBlitPixEPX16;			break;
1386 				case VIDEOMODE_HQ2X:		blitFn = S9xBlitPixHQ2x16;			break;
1387 			}
1388 		}
1389 	}
1390 	else
1391 	if (height <= SNES_HEIGHT_EXTENDED)
1392 	{
1393 		copyWidth  = width;
1394 		copyHeight = height * 2;
1395 
1396 		switch (GUI.video_mode)
1397 		{
1398 			default:					blitFn = S9xBlitPixSimple1x2;	break;
1399 			case VIDEOMODE_TV:			blitFn = S9xBlitPixTV1x2;		break;
1400 		}
1401 	}
1402 	else
1403 	{
1404 		copyWidth  = width;
1405 		copyHeight = height;
1406 		blitFn = S9xBlitPixSimple1x1;
1407 	}
1408 	blitFn((uint8 *) GFX.Screen, GFX.Pitch, GUI.blit_screen, GUI.blit_screen_pitch, width, height);
1409 
1410 	if (height < prevHeight)
1411 	{
1412 		int	p = GUI.blit_screen_pitch >> 2;
1413 		for (int y = SNES_HEIGHT * 2; y < SNES_HEIGHT_EXTENDED * 2; y++)
1414 		{
1415 			uint32	*d = (uint32 *) (GUI.blit_screen + y * GUI.blit_screen_pitch);
1416 			for (int x = 0; x < p; x++)
1417 				*d++ = 0;
1418 		}
1419 	}
1420 
1421 #ifdef USE_XVIDEO
1422 	// Adjust source blit region if SNES would only fill half the screen.
1423 	if (height <= SNES_HEIGHT_EXTENDED)
1424 		GUI.imageHeight = height * 2;
1425 
1426 	if (GUI.use_xvideo && (GUI.xv_format == FOURCC_YUY2))
1427 	{
1428 		uint16 *s = (uint16 *)GUI.blit_screen;
1429 		uint8 *d = (uint8 *)GUI.image->data;
1430 
1431 		// convert GUI.blit_screen and copy to XV image
1432 		for (int y = 0; y < copyHeight; y++)
1433 		{
1434 			for (int x = 0; x < SNES_WIDTH * 2; x += 2)
1435 			{
1436 				// Read two RGB pxls
1437 				//  TODO: there is an assumption of endianness here...
1438 				// ALSO todo:  The 0x7FFF works around some issue with S9xPutChar, where
1439 				//  despite asking for RGB555 in InitImage, it insists on drawing with RGB565 instead.
1440 				//  This may discolor messages but at least it doesn't overflow yuv-tables and crash.
1441 				unsigned short rgb1 = (*s & 0x7FFF); s++;
1442 				unsigned short rgb2 = (*s & 0x7FFF); s++;
1443 
1444 				// put two YUYV pxls
1445 				// lum1
1446 				*d = GUI.y_table[rgb1]; d++;
1447 				// U
1448 				*d = (GUI.u_table[rgb1] + GUI.u_table[rgb2]) / 2; d++;
1449 				// lum2
1450 				*d = GUI.y_table[rgb2]; d++;
1451 				// V
1452 				*d = (GUI.v_table[rgb1] + GUI.v_table[rgb2]) / 2; d++;
1453 			}
1454 		}
1455 	}
1456 	else
1457 #endif
1458 	if (GUI.need_convert)
1459 	{
1460 		if (GUI.bytes_per_pixel == 3)
1461 			Convert16To24Packed(copyWidth, copyHeight);
1462 		else
1463 			Convert16To24(copyWidth, copyHeight);
1464 	}
1465 
1466 	Repaint(TRUE);
1467 
1468 	prevWidth  = width;
1469 	prevHeight = height;
1470 }
1471 
Convert16To24(int width,int height)1472 static void Convert16To24 (int width, int height)
1473 {
1474 	if (GUI.pixel_format == 565)
1475 	{
1476 		for (int y = 0; y < height; y++)
1477 		{
1478 			uint16	*s = (uint16 *) (GUI.blit_screen + y * GUI.blit_screen_pitch);
1479 			uint32	*d = (uint32 *) (GUI.image->data + y * GUI.image->bytes_per_line);
1480 
1481 			for (int x = 0; x < width; x++)
1482 			{
1483 				uint32	pixel = *s++;
1484 				*d++ = (((pixel >> 11) & 0x1f) << (GUI.red_shift + 3)) | (((pixel >> 6) & 0x1f) << (GUI.green_shift + 3)) | ((pixel & 0x1f) << (GUI.blue_shift + 3));
1485 			}
1486 		}
1487 	}
1488 	else
1489 	{
1490 		for (int y = 0; y < height; y++)
1491 		{
1492 			uint16	*s = (uint16 *) (GUI.blit_screen + y * GUI.blit_screen_pitch);
1493 			uint32	*d = (uint32 *) (GUI.image->data + y * GUI.image->bytes_per_line);
1494 
1495 			for (int x = 0; x < width; x++)
1496 			{
1497 				uint32	pixel = *s++;
1498 				*d++ = (((pixel >> 10) & 0x1f) << (GUI.red_shift + 3)) | (((pixel >> 5) & 0x1f) << (GUI.green_shift + 3)) | ((pixel & 0x1f) << (GUI.blue_shift + 3));
1499 			}
1500 		}
1501 	}
1502 }
1503 
Convert16To24Packed(int width,int height)1504 static void Convert16To24Packed (int width, int height)
1505 {
1506 	if (GUI.pixel_format == 565)
1507 	{
1508 		for (int y = 0; y < height; y++)
1509 		{
1510 			uint16	*s = (uint16 *) (GUI.blit_screen + y * GUI.blit_screen_pitch);
1511 			uint8	*d = (uint8 *)  (GUI.image->data + y * GUI.image->bytes_per_line);
1512 
1513 		#ifdef LSB_FIRST
1514 			if (GUI.red_shift < GUI.blue_shift)
1515 		#else
1516 			if (GUI.red_shift > GUI.blue_shift)
1517 		#endif
1518 			{
1519 				for (int x = 0; x < width; x++)
1520 				{
1521 					uint32	pixel = *s++;
1522 					*d++ = (pixel >> (11 - 3)) & 0xf8;
1523 					*d++ = (pixel >>  (6 - 3)) & 0xf8;
1524 					*d++ = (pixel & 0x1f) << 3;
1525 				}
1526 			}
1527 			else
1528 			{
1529 				for (int x = 0; x < width; x++)
1530 				{
1531 					uint32	pixel = *s++;
1532 					*d++ = (pixel & 0x1f) << 3;
1533 					*d++ = (pixel >>  (6 - 3)) & 0xf8;
1534 					*d++ = (pixel >> (11 - 3)) & 0xf8;
1535 				}
1536 			}
1537 		}
1538 	}
1539 	else
1540 	{
1541 		for (int y = 0; y < height; y++)
1542 		{
1543 			uint16	*s = (uint16 *) (GUI.blit_screen + y * GUI.blit_screen_pitch);
1544 			uint8	*d = (uint8 *)  (GUI.image->data + y * GUI.image->bytes_per_line);
1545 
1546 		#ifdef LSB_FIRST
1547 			if (GUI.red_shift < GUI.blue_shift)
1548 		#else
1549 			if (GUI.red_shift > GUI.blue_shift)
1550 		#endif
1551 			{
1552 				for (int x = 0; x < width; x++)
1553 				{
1554 					uint32	pixel = *s++;
1555 					*d++ = (pixel >> (10 - 3)) & 0xf8;
1556 					*d++ = (pixel >>  (5 - 3)) & 0xf8;
1557 					*d++ = (pixel & 0x1f) << 3;
1558 				}
1559 			}
1560 			else
1561 			{
1562 				for (int x = 0; x < width; x++)
1563 				{
1564 					uint32	pixel = *s++;
1565 					*d++ = (pixel & 0x1f) << 3;
1566 					*d++ = (pixel >>  (5 - 3)) & 0xf8;
1567 					*d++ = (pixel >> (10 - 3)) & 0xf8;
1568 				}
1569 			}
1570 		}
1571 	}
1572 }
1573 
Repaint(bool8 isFrameBoundry)1574 static void Repaint (bool8 isFrameBoundry)
1575 {
1576 #ifdef USE_XVIDEO
1577 	if (GUI.use_xvideo)
1578 	{
1579 #ifdef MITSHM
1580 		if (GUI.use_shared_memory)
1581 		{
1582 			XvShmPutImage(GUI.display, GUI.xv_port, GUI.window, GUI.gc, GUI.image->xvimage,
1583 				0, 0, SNES_WIDTH * 2, GUI.imageHeight,
1584 				GUI.x_offset, GUI.y_offset, GUI.scale_w, GUI.scale_h, False);
1585 		}
1586 		else
1587 #endif
1588 		XvPutImage(GUI.display, GUI.xv_port, GUI.window, GUI.gc, GUI.image->xvimage,
1589 			0, 0, SNES_WIDTH * 2, GUI.imageHeight,
1590 			GUI.x_offset, GUI.y_offset, GUI.scale_w, GUI.scale_h);
1591 	}
1592 	else
1593 #endif
1594 #ifdef MITSHM
1595 	if (GUI.use_shared_memory)
1596 	{
1597 		XShmPutImage(GUI.display, GUI.window, GUI.gc, GUI.image->ximage, 0, 0, GUI.x_offset, GUI.y_offset, SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED * 2, False);
1598 		XSync(GUI.display, False);
1599 	}
1600 	else
1601 #endif
1602 		XPutImage(GUI.display, GUI.window, GUI.gc, GUI.image->ximage, 0, 0, GUI.x_offset, GUI.y_offset, SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED * 2);
1603 
1604 	Window			root, child;
1605 	int				root_x, root_y, x, y;
1606 	unsigned int	mask;
1607 
1608 	// Use QueryPointer to sync X server and as a side effect also gets current pointer position for SNES mouse emulation.
1609 	XQueryPointer(GUI.display, GUI.window, &root, &child, &root_x, &root_y, &x, &y, &mask);
1610 
1611 	if (x >= 0 && y >= 0 && x < SNES_WIDTH * 2 && y < SNES_HEIGHT_EXTENDED * 2)
1612 	{
1613 		GUI.mouse_x = x >> 1;
1614 		GUI.mouse_y = y >> 1;
1615 
1616 		if (mask & Mod1Mask)
1617 		{
1618 			if (!GUI.mod1_pressed)
1619 			{
1620 				GUI.mod1_pressed = TRUE;
1621 				XDefineCursor(GUI.display, GUI.window, GUI.cross_hair_cursor);
1622 			}
1623 		}
1624 		else
1625 		if (GUI.mod1_pressed)
1626 		{
1627 			GUI.mod1_pressed = FALSE;
1628 			XDefineCursor(GUI.display, GUI.window, GUI.point_cursor);
1629 		}
1630 	}
1631 
1632 	if (Settings.DumpStreams && isFrameBoundry)
1633 		S9xVideoLogger(GUI.image->data, SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED * 2, GUI.bytes_per_pixel, GUI.image->bytes_per_line);
1634 }
1635 
S9xTextMode(void)1636 void S9xTextMode (void)
1637 {
1638 	SetXRepeat(TRUE);
1639 }
1640 
S9xGraphicsMode(void)1641 void S9xGraphicsMode (void)
1642 {
1643 	SetXRepeat(FALSE);
1644 }
1645 
CheckForPendingXEvents(Display * display)1646 static bool8 CheckForPendingXEvents (Display *display)
1647 {
1648 #ifdef SELECT_BROKEN_FOR_SIGNALS
1649 	int	arg = 0;
1650 
1651 	return (XEventsQueued(display, QueuedAlready) || (ioctl(ConnectionNumber(display), FIONREAD, &arg) == 0 && arg));
1652 #else
1653 	return (XPending(display));
1654 #endif
1655 }
1656 
S9xProcessEvents(bool8 block)1657 void S9xProcessEvents (bool8 block)
1658 {
1659 	while (block || CheckForPendingXEvents(GUI.display))
1660 	{
1661 		XEvent	event;
1662 
1663 		XNextEvent(GUI.display, &event);
1664 		block = FALSE;
1665 
1666 		switch (event.type)
1667 		{
1668 			case KeyPress:
1669 			case KeyRelease:
1670 				S9xReportButton(((event.xkey.state & (ShiftMask | Mod1Mask | ControlMask | Mod4Mask)) << 8) | event.xkey.keycode, event.type == KeyPress);
1671 			#if 1
1672 				{
1673 					KeyCode	kc = XKeysymToKeycode(GUI.display, XKeycodeToKeysym(GUI.display, event.xkey.keycode, 0));
1674 					if (event.xkey.keycode != kc)
1675 						S9xReportButton(((event.xkey.state & (ShiftMask | Mod1Mask | ControlMask | Mod4Mask)) << 8) | kc, event.type == KeyPress);
1676 				}
1677 			#endif
1678 				break;
1679 
1680 			case FocusIn:
1681 				SetXRepeat(FALSE);
1682 				XFlush(GUI.display);
1683 				break;
1684 
1685 			case FocusOut:
1686 				SetXRepeat(TRUE);
1687 				XFlush(GUI.display);
1688 				break;
1689 
1690 			case ConfigureNotify:
1691 				break;
1692 
1693 			case ButtonPress:
1694 			case ButtonRelease:
1695 				S9xReportButton(0x40000000 | (event.xbutton.button - 1), event.type == ButtonPress);
1696 				break;
1697 
1698 			case Expose:
1699 				Repaint(FALSE);
1700 				break;
1701 		}
1702 	}
1703 }
1704 
S9xSelectFilename(const char * def,const char * dir1,const char * ext1,const char * title)1705 const char * S9xSelectFilename (const char *def, const char *dir1, const char *ext1, const char *title)
1706 {
1707 	static char	s[PATH_MAX + 1];
1708 	char		buffer[PATH_MAX + 1];
1709 
1710 	SetXRepeat(TRUE);
1711 
1712 	printf("\n%s (default: %s): ", title, def);
1713 	fflush(stdout);
1714 
1715 	SetXRepeat(FALSE);
1716 
1717 	if (fgets(buffer, PATH_MAX + 1, stdin))
1718 	{
1719 		char	drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
1720 
1721 		char	*p = buffer;
1722 		while (isspace(*p))
1723 			p++;
1724 		if (!*p)
1725 		{
1726 			strncpy(buffer, def, PATH_MAX + 1);
1727 			buffer[PATH_MAX] = 0;
1728 			p = buffer;
1729 		}
1730 
1731 		char	*q = strrchr(p, '\n');
1732 		if (q)
1733 			*q = 0;
1734 
1735 		_splitpath(p, drive, dir, fname, ext);
1736 		_makepath(s, drive, *dir ? dir : dir1, fname, *ext ? ext : ext1);
1737 
1738 		return (s);
1739 	}
1740 
1741 	return (NULL);
1742 }
1743 
S9xMessage(int type,int number,const char * message)1744 void S9xMessage (int type, int number, const char *message)
1745 {
1746 	const int	max = 36 * 3;
1747 	static char	buffer[max + 1];
1748 
1749 	fprintf(stdout, "%s\n", message);
1750 	strncpy(buffer, message, max + 1);
1751 	buffer[max] = 0;
1752 	S9xSetInfoString(buffer);
1753 }
1754 
S9xStringInput(const char * message)1755 const char * S9xStringInput (const char *message)
1756 {
1757 	static char	buffer[256];
1758 
1759 	printf("%s: ", message);
1760 	fflush(stdout);
1761 
1762 	if (fgets(buffer, sizeof(buffer) - 2, stdin))
1763 		return (buffer);
1764 
1765 	return (NULL);
1766 }
1767 
S9xSetTitle(const char * string)1768 void S9xSetTitle (const char *string)
1769 {
1770 	XStoreName(GUI.display, GUI.window, string);
1771 	XFlush(GUI.display);
1772 }
1773 
SetXRepeat(bool8 state)1774 static void SetXRepeat (bool8 state)
1775 {
1776 	if (state)
1777 		XAutoRepeatOn(GUI.display);
1778 	else
1779 		XAutoRepeatOff(GUI.display);
1780 }
1781 
S9xGetDisplayCommandT(const char * n)1782 s9xcommand_t S9xGetDisplayCommandT (const char *n)
1783 {
1784 	s9xcommand_t	cmd;
1785 
1786 	cmd.type         = S9xBadMapping;
1787 	cmd.multi_press  = 0;
1788 	cmd.button_norpt = 0;
1789 	cmd.port[0]      = 0xff;
1790 	cmd.port[1]      = 0;
1791 	cmd.port[2]      = 0;
1792 	cmd.port[3]      = 0;
1793 
1794 	return (cmd);
1795 }
1796 
S9xGetDisplayCommandName(s9xcommand_t cmd)1797 char * S9xGetDisplayCommandName (s9xcommand_t cmd)
1798 {
1799 	return (strdup("None"));
1800 }
1801 
S9xHandleDisplayCommand(s9xcommand_t cmd,int16 data1,int16 data2)1802 void S9xHandleDisplayCommand (s9xcommand_t cmd, int16 data1, int16 data2)
1803 {
1804 	return;
1805 }
1806 
S9xMapDisplayInput(const char * n,s9xcommand_t * cmd)1807 bool8 S9xMapDisplayInput (const char *n, s9xcommand_t *cmd)
1808 {
1809 	int	i, d;
1810 
1811 	if (!isdigit(n[1]) || !isdigit(n[2]) || n[3] != ':')
1812 		goto unrecog;
1813 
1814 	d = ((n[1] - '0') * 10 + (n[2] - '0')) << 24;
1815 
1816 	switch (n[0])
1817 	{
1818 		case 'K':
1819 		{
1820 			KeyCode	kc;
1821 			KeySym	ks;
1822 
1823 			d |= 0x00000000;
1824 
1825 			for (i = 4; n[i] != '\0' && n[i] != '+'; i++) ;
1826 
1827 			if (n[i] == '\0' || i == 4)
1828 				i = 4;
1829 			else
1830 			{
1831 				for (i = 4; n[i] != '+'; i++)
1832 				{
1833 					switch (n[i])
1834 					{
1835 						case 'S': d |= ShiftMask   << 8; break;
1836 						case 'C': d |= ControlMask << 8; break;
1837 						case 'A': d |= Mod1Mask    << 8; break;
1838 						case 'M': d |= Mod4Mask    << 8; break;
1839 						default:  goto unrecog;
1840 					}
1841 				}
1842 
1843 				i++;
1844 			}
1845 
1846 			if ((ks = XStringToKeysym(n + i)) == NoSymbol)
1847 				goto unrecog;
1848 			if ((kc = XKeysymToKeycode(GUI.display, ks)) == 0)
1849 				goto unrecog;
1850 
1851 			d |= kc & 0xff;
1852 
1853 			return (S9xMapButton(d, *cmd, false));
1854 		}
1855 
1856 		case 'M':
1857 		{
1858 			char	*c;
1859 			int		j;
1860 
1861 			d |= 0x40000000;
1862 
1863 			if (!strncmp(n + 4, "Pointer", 7))
1864 			{
1865 				d |= 0x8000;
1866 
1867 				if (n[11] == '\0')
1868 					return (S9xMapPointer(d, *cmd, true));
1869 
1870 				i = 11;
1871 			}
1872 			else
1873 			if (n[4] == 'B')
1874 				i = 5;
1875 			else
1876 				goto unrecog;
1877 
1878 			d |= j = strtol(n + i, &c, 10);
1879 
1880 			if ((c != NULL && *c != '\0') || j > 0x7fff)
1881 				goto unrecog;
1882 
1883 			if (d & 0x8000)
1884 				return (S9xMapPointer(d, *cmd, true));
1885 
1886 			return (S9xMapButton(d, *cmd, false));
1887 		}
1888 
1889 		default:
1890 			break;
1891 	}
1892 
1893 unrecog:
1894 	char	*err = new char[strlen(n) + 34];
1895 
1896 	sprintf(err, "Unrecognized input device name '%s'", n);
1897 	perror(err);
1898 	delete [] err;
1899 
1900 	return (false);
1901 }
1902 
S9xDisplayPollButton(uint32 id,bool * pressed)1903 bool S9xDisplayPollButton (uint32 id, bool *pressed)
1904 {
1905 	return (false);
1906 }
1907 
S9xDisplayPollAxis(uint32 id,int16 * value)1908 bool S9xDisplayPollAxis (uint32 id, int16 *value)
1909 {
1910 	return (false);
1911 }
1912 
S9xDisplayPollPointer(uint32 id,int16 * x,int16 * y)1913 bool S9xDisplayPollPointer (uint32 id, int16 *x, int16 *y)
1914 {
1915 	if ((id & 0xc0008000) != 0x40008000)
1916 		return (false);
1917 
1918 	int	d = (id >> 24) & 0x3f,
1919 		n = id & 0x7fff;
1920 
1921 	if (d != 0 || n != 0)
1922 		return (false);
1923 
1924 	*x = GUI.mouse_x;
1925 	*y = GUI.mouse_y;
1926 
1927 	return (true);
1928 }
1929 
S9xSetPalette(void)1930 void S9xSetPalette (void)
1931 {
1932 	return;
1933 }
1934