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