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