1 /*
2 * video_x.c
3 *
4 * display screen management
5 */
6
7 /* $Id: video_x.c,v 1.26 2000/09/16 16:01:59 nyef Exp $ */
8
9 #include <X11/Xlib.h>
10 #include <X11/keysym.h>
11 #include <X11/Intrinsic.h>
12 #include <X11/StringDefs.h>
13 #include <X11/extensions/XShm.h>
14 #include <sys/shm.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include "video.h"
18 #include "tool.h"
19 #include "ui.h"
20
21 /* the joypad (only one, I could care less about more) */
22 struct joypad *ui_joypad;
23
24 /* the keypad (only one, I could care less about more) */
25 struct keypad *ui_keypad;
26
27 int video_active = 0;
28 int deblevel = 0;
29
30 Display *display;
31 Window nes_window;
32 GC nes_gc;
33 Colormap nes_cmap;
34 Screen *nes_screen;
35
36 int buffer_x;
37 int buffer_y;
38
39 struct image_buffer {
40 int is_shm;
41 char *data;
42 XImage *image;
43 } image_buffers[2], *cur_image_buffer;
44
45 typedef void ((*xlatfunc)(void *, int));
46
47 xlatfunc vidxlate;
48 void vidxlate16 (char *dest, int size);
49 typedef void (*video_setpal_t)(int, int *r, int *g, int *b);
50 video_setpal_t video_setpal_true;
51 void init_xlate8(int colors, int *red, int *green, int *blue);
52 void init_xlate16(int colors, int *red, int *green, int *blue);
53 void init_xlate32(int colors, int *red, int *green, int *blue);
54 unsigned char *vid_pre_xlat;
55
56 /* FIXME: ui_x.c interface -- move to separate header. */
57 extern Widget drawbox;
58 extern Widget manager;
59 void ui_set_drawbox_size(int width, int height);
60
61 void do_key_event(Widget, XtPointer, XEvent *, Boolean *);
62
63 /*
64 * Thanks to Sam Lantinga of SDL and the GDK team for XShm references.
65 */
66 int use_shm = 0;
67 int shm_error = 0;
68 int (*old_x_handler)(Display *, XErrorEvent *);
69 int alloc_shm_surface(struct image_buffer *, int, int, int, int);
70 int shm_error_handler(Display*, XErrorEvent *);
71
video_init()72 void video_init()
73 {
74 int is_local_display;
75
76 display = XtDisplay(drawbox);
77 nes_screen = XtScreen(drawbox);
78 nes_window = XtWindow(drawbox);
79
80 ui_set_drawbox_size(256, 240);
81
82 nes_gc = DefaultGCOfScreen(nes_screen);
83 XtVaGetValues(drawbox, XtNcolormap, &nes_cmap, NULL);
84
85 XtAddEventHandler(manager, KeyPressMask | KeyReleaseMask, False, do_key_event, NULL);
86
87 XFlush(display);
88
89 if (0 == strncmp(XDisplayName(NULL), ":", 1)) {
90 is_local_display = True;
91 } else {
92 is_local_display = False;
93 }
94
95 if (is_local_display) {
96 use_shm = XShmQueryExtension(display);
97 } else {
98 use_shm = 0;
99 }
100
101 #ifdef FORCE_NOXSHM
102 use_shm = 0;
103 #endif
104
105 if (DefaultDepthOfScreen(nes_screen) == 8) {
106 video_setpal_true = init_xlate8;
107 } else if (DefaultDepthOfScreen(nes_screen) == 16) {
108 video_setpal_true = init_xlate16;
109 } else if (DefaultDepthOfScreen(nes_screen) == 24) {
110 video_setpal_true = init_xlate32;
111 } else {
112 fprintf(stderr, "display depth %d unsupported.\n", DefaultDepthOfScreen(nes_screen));
113 exit(1);
114 }
115 }
116
video_shutdown(void)117 void video_shutdown(void)
118 {
119 }
120
video_allocate_image_buffer(struct image_buffer * buffer,int bit_depth,int bytes_per_pixel,int x,int y)121 void video_allocate_image_buffer(struct image_buffer *buffer, int bit_depth, int bytes_per_pixel, int x, int y)
122 {
123 if (use_shm) {
124 if (alloc_shm_surface(buffer, bit_depth, bytes_per_pixel, x, y)) {
125 return;
126 } else {
127 deb_printf("shm failure, falling back.\n");
128 use_shm = 0;
129 }
130 }
131
132 buffer->data = malloc(x * y * bytes_per_pixel);
133 buffer->image = XCreateImage(display, DefaultVisualOfScreen(nes_screen),
134 bit_depth, ZPixmap, 0, buffer->data, x, y, 8, 0);
135 buffer->is_shm = 0;
136 }
137
video_setsize(int x,int y)138 void video_setsize(int x, int y)
139 {
140 buffer_x = x;
141 buffer_y = y;
142
143 ui_set_drawbox_size(x, y);
144
145 if (DefaultDepthOfScreen(nes_screen) == 8) {
146 video_allocate_image_buffer(&image_buffers[0], 8, 1, x, y);
147 video_allocate_image_buffer(&image_buffers[1], 8, 1, x, y);
148 } else if (DefaultDepthOfScreen(nes_screen) == 16) {
149 video_allocate_image_buffer(&image_buffers[0], 16, 2, x, y);
150 video_allocate_image_buffer(&image_buffers[1], 16, 2, x, y);
151 } else if (DefaultDepthOfScreen(nes_screen) == 24) {
152 video_allocate_image_buffer(&image_buffers[0], 24, 4, x, y);
153 video_allocate_image_buffer(&image_buffers[1], 24, 4, x, y);
154 }
155
156 cur_image_buffer = &image_buffers[0];
157 }
158
shm_error_handler(Display * d,XErrorEvent * e)159 int shm_error_handler(Display* d, XErrorEvent* e)
160 {
161 if (BadAccess == e->error_code) {
162 shm_error = 1;
163 return 0;
164 }
165
166 return old_x_handler(d, e);
167 }
168
alloc_shm_surface(struct image_buffer * buffer,int depth,int bytes_per_pixel,int width,int height)169 int alloc_shm_surface(struct image_buffer *buffer, int depth, int bytes_per_pixel, int width, int height)
170 {
171 XShmSegmentInfo *shm_info;
172 Screen *screen = XtScreen(drawbox);
173
174 shm_info = calloc(1, sizeof(XShmSegmentInfo));
175
176 shm_info->shmid = shmget(IPC_PRIVATE, width * height * bytes_per_pixel,
177 IPC_CREAT | 0777);
178
179 if (shm_info->shmid < 0) {
180 perror("darcnes: shmget()");
181 use_shm = 0;
182 return 0;
183 }
184
185 shm_info->shmaddr = (char *)shmat(shm_info->shmid, 0, 0);
186 shm_info->readOnly = False;
187
188 if ((char *) -1 == shm_info->shmaddr) {
189 perror("darcnes: shmat()");
190
191 /* destroy the SHM block now, before we forget */
192 if (shmctl(shm_info->shmid, IPC_RMID, NULL) < 0) {
193 perror("darcnes: shmctl(RMID)");
194 /* No, this isn't fatal enough to cause us to quit */
195 }
196
197 use_shm = 0;
198 return 0;
199 }
200
201 shm_error = 0;
202 /*
203 * We set this to grab anything bad that happens during the
204 * attach and (especially) sync calls.
205 */
206 old_x_handler = XSetErrorHandler(shm_error_handler);
207 XShmAttach(display, shm_info);
208 XSync(display, False);
209 /* And then reset the old handler here. */
210 XSetErrorHandler(old_x_handler);
211
212 /* Automatically destroy surface on detach. */
213 if (shmctl(shm_info->shmid, IPC_RMID, NULL) < 0) {
214 perror("darcnes: shmctl(RMID)");
215 /* No, this isn't fatal enough to cause us to quit */
216 }
217
218 if (shm_error) {
219 fprintf(stderr, "darcnes: error during SHM attach.\n");
220 shmdt(shm_info->shmaddr);
221 use_shm = 0;
222 return 0;
223 }
224
225 buffer->data = shm_info->shmaddr;
226 buffer->image = XShmCreateImage(display, DefaultVisualOfScreen(screen), depth,
227 ZPixmap, shm_info->shmaddr, shm_info, width, height);
228 buffer->is_shm = 1;
229
230 return 1;
231 }
232
video_display_buffer(void)233 void video_display_buffer(void)
234 {
235 if (vidxlate) {
236 vidxlate(cur_image_buffer->data, buffer_x * buffer_y);
237 }
238
239 if (cur_image_buffer->is_shm) {
240 XShmPutImage(display, nes_window, nes_gc, cur_image_buffer->image,
241 0, 0, 0, 0, buffer_x, buffer_y, False);
242 } else {
243 XPutImage(display, nes_window, nes_gc, cur_image_buffer->image, 0, 0, 0, 0, buffer_x, buffer_y);
244 }
245
246 XFlush(display);
247
248 if (cur_image_buffer == &image_buffers[0]) {
249 cur_image_buffer = &image_buffers[1];
250 } else {
251 cur_image_buffer = &image_buffers[0];
252 }
253 }
254
255 /* joypad emulation */
256
ui_register_joypad(struct joypad * pad)257 int ui_register_joypad(struct joypad *pad)
258 {
259 if (ui_joypad) {
260 return 0;
261 }
262
263 ui_joypad = pad;
264 return 1;
265 }
266
ui_update_joypad(struct joypad * pad)267 void ui_update_joypad(struct joypad *pad)
268 {
269 /* NOTE: does nothing. may want to do stuff when using real gamepads */
270 }
271
video_events(void)272 void video_events(void)
273 {
274 }
275
276 char joypad_key_state[12];
277
278 struct {
279 KeySym keysym;
280 int state;
281 } joypad_keys[] = {
282 {XK_s, 8}, {XK_S, 8},
283 {XK_a, 9}, {XK_A, 9},
284 {XK_bracketleft, 10}, {XK_braceleft, 10},
285 {XK_bracketright, 11}, {XK_braceright, 11},
286 {XK_Up, 0}, {XK_KP_Up, 4},
287 {XK_Down, 1}, {XK_KP_Down, 5},
288 {XK_Left, 2}, {XK_KP_Left, 6},
289 {XK_Right, 3}, {XK_KP_Right, 7},
290 };
291
handle_joypad_key(int type,KeySym keysym)292 void handle_joypad_key(int type, KeySym keysym)
293 {
294 int i;
295
296 for (i = (sizeof(joypad_keys) / sizeof(joypad_keys[0])) - 1; i >= 0; i--) {
297 if (keysym == joypad_keys[i].keysym) {
298 if (type == KeyPress) {
299 joypad_key_state[joypad_keys[i].state] = 1;
300 } else {
301 joypad_key_state[joypad_keys[i].state] = 0;
302 }
303 }
304 }
305
306 if (!ui_joypad) {
307 return;
308 }
309
310 switch (ui_joypad->button_template->num_buttons) {
311 /* NOTE: all cases fall through */
312 case 8:
313 if (joypad_key_state[11]) {
314 ui_joypad->data |= ui_joypad->button_template->buttons[7];
315 } else {
316 ui_joypad->data &= ~ui_joypad->button_template->buttons[7];
317 }
318
319 case 7:
320 if (joypad_key_state[10]) {
321 ui_joypad->data |= ui_joypad->button_template->buttons[6];
322 } else {
323 ui_joypad->data &= ~ui_joypad->button_template->buttons[6];
324 }
325
326 case 6:
327 if (joypad_key_state[9]) {
328 ui_joypad->data |= ui_joypad->button_template->buttons[5];
329 } else {
330 ui_joypad->data &= ~ui_joypad->button_template->buttons[5];
331 }
332
333 case 5:
334 if (joypad_key_state[8]) {
335 ui_joypad->data |= ui_joypad->button_template->buttons[4];
336 } else {
337 ui_joypad->data &= ~ui_joypad->button_template->buttons[4];
338 }
339
340 case 4:
341 if (joypad_key_state[7] || joypad_key_state[3]) {
342 ui_joypad->data |= ui_joypad->button_template->buttons[3];
343 } else {
344 ui_joypad->data &= ~ui_joypad->button_template->buttons[3];
345 }
346
347 case 3:
348 if (joypad_key_state[6] || joypad_key_state[2]) {
349 ui_joypad->data |= ui_joypad->button_template->buttons[2];
350 } else {
351 ui_joypad->data &= ~ui_joypad->button_template->buttons[2];
352 }
353
354 case 2:
355 if (joypad_key_state[5] || joypad_key_state[1]) {
356 ui_joypad->data |= ui_joypad->button_template->buttons[1];
357 } else {
358 ui_joypad->data &= ~ui_joypad->button_template->buttons[1];
359 }
360
361 case 1:
362 if (joypad_key_state[4] || joypad_key_state[0]) {
363 ui_joypad->data |= ui_joypad->button_template->buttons[0];
364 } else {
365 ui_joypad->data &= ~ui_joypad->button_template->buttons[0];
366 }
367
368 default: break;
369 }
370 }
371
372 /* keypad emulation */
373
keypad_register(struct keypad * pad)374 int keypad_register(struct keypad *pad)
375 {
376 if (ui_keypad) {
377 return 0;
378 }
379
380 ui_keypad = pad;
381 return 1;
382 }
383
keypad_update(struct keypad * pad)384 void keypad_update(struct keypad *pad)
385 {
386 /* NOTE: does nothing. may want to do stuff when using real keypads */
387 }
388
389 u16 keypad_data;
390
391 struct {
392 KeySym keysym;
393 u16 state;
394 } keypad_keys[12] = {
395 {XK_KP_0, KPD_0},
396 {XK_KP_1, KPD_1},
397 {XK_KP_2, KPD_2},
398 {XK_KP_3, KPD_3},
399 {XK_KP_4, KPD_4},
400 {XK_KP_5, KPD_5},
401 {XK_KP_6, KPD_6},
402 {XK_KP_7, KPD_7},
403 {XK_KP_8, KPD_8},
404 {XK_KP_9, KPD_9},
405 {XK_KP_Multiply, KPD_STAR},
406 {XK_KP_Add, KPD_HASH},
407 };
408
handle_keypad_key(int type,KeySym keysym)409 void handle_keypad_key(int type, KeySym keysym)
410 {
411 int i;
412
413 for (i = (sizeof(keypad_keys) / sizeof(keypad_keys[0])) - 1; i >= 0; i--) {
414 if (keysym == keypad_keys[i].keysym) {
415 if (type == KeyPress) {
416 keypad_data |= keypad_keys[i].state;
417 } else {
418 keypad_data &= ~keypad_keys[i].state;
419 }
420 }
421 }
422
423 if (!ui_keypad) {
424 return;
425 }
426
427 ui_keypad->data = keypad_data;
428 }
429
430 /* keyboard emulation */
431
432 typedef void (*keyhook)(Display *display, XEvent *event);
433
434 keyhook ui_keyhook;
435
ui_set_keyboard_hook(keyhook hook)436 void ui_set_keyboard_hook(keyhook hook)
437 {
438 ui_keyhook = hook;
439 }
440
441 /* key event handler */
442
do_key_event(Widget w,XtPointer client_data,XEvent * event,Boolean * continue_dispatch)443 void do_key_event(Widget w, XtPointer client_data, XEvent *event, Boolean *continue_dispatch)
444 {
445 KeySym keysym;
446 Modifiers dummy;
447
448 XtTranslateKeycode(display, event->xkey.keycode, event->xkey.state, &dummy, &keysym);
449
450 if (ui_keyhook) {
451 ui_keyhook(display, event);
452 }
453
454 handle_joypad_key(event->type, keysym);
455 handle_keypad_key(event->type, keysym);
456 }
457
458 short *xlatepal;
459
vidxlate16(char * dest,int size)460 void vidxlate16(char *dest, int size)
461 {
462 int i;
463
464 for (i = size; i > 0; i--) {
465 *(((short *)dest)+(i-1)) = xlatepal[(int) *(dest+(i-1))];
466 }
467 }
468
469 long *xlatepal2;
470
vidxlate32(char * dest,int size)471 void vidxlate32(char *dest, int size)
472 {
473 int i;
474
475 for (i = size; i > 0; i--) {
476 *(((long *)dest)+(i-1)) = xlatepal2[(int) *(dest+(i-1))];
477 }
478 }
479
video_get_vbp(int line)480 unsigned char *video_get_vbp(int line)
481 {
482 return cur_image_buffer->data + (line * buffer_x);
483 }
484
485 /* #include "nespal.c" */
486
init_xlate8(int colors,int * red,int * green,int * blue)487 void init_xlate8(int colors, int *red, int *green, int *blue)
488 {
489 int i;
490 XColor color;
491
492 vid_pre_xlat = malloc(colors);
493 vidxlate = NULL;
494
495 for (i = 0; i < colors; i++) {
496 color.flags = DoRed | DoGreen | DoBlue;
497 color.red = red[i];
498 color.green = green[i];
499 color.blue = blue[i];
500 color.red += color.red << 8;
501 color.green += color.green << 8;
502 color.blue += color.blue << 8;
503 if (XAllocColor(display, nes_cmap, &color)) {
504 vid_pre_xlat[i] = color.pixel;
505 }
506 }
507 }
508
init_xlate16(int colors,int * red,int * green,int * blue)509 void init_xlate16(int colors, int *red, int *green, int *blue)
510 {
511 int i;
512 XColor color;
513
514 xlatepal = malloc(colors << 1);
515 vid_pre_xlat = malloc(colors);
516 vidxlate = (xlatfunc)&vidxlate16;
517
518 for (i = 0; i < colors; i++) {
519 vid_pre_xlat[i] = i;
520 color.flags = DoRed | DoGreen | DoBlue;
521 color.red = red[i];
522 color.green = green[i];
523 color.blue = blue[i];
524 color.red += color.red << 8;
525 color.green += color.green << 8;
526 color.blue += color.blue << 8;
527 if (XAllocColor(display, nes_cmap, &color)) {
528 xlatepal[i] = color.pixel;
529 }
530 }
531 }
532
init_xlate32(int colors,int * red,int * green,int * blue)533 void init_xlate32(int colors, int *red, int *green, int *blue)
534 {
535 int i;
536 XColor color;
537
538 xlatepal2 = malloc(colors << 2);
539 vid_pre_xlat = malloc(colors);
540 vidxlate = (xlatfunc)&vidxlate32;
541
542 for (i = 0; i < colors; i++) {
543 vid_pre_xlat[i] = i;
544 color.flags = DoRed | DoGreen | DoBlue;
545 color.red = red[i];
546 color.green = green[i];
547 color.blue = blue[i];
548 color.red += color.red << 8;
549 color.green += color.green << 8;
550 color.blue += color.blue << 8;
551 if (XAllocColor(display, nes_cmap, &color)) {
552 xlatepal2[i] = color.pixel;
553 }
554 }
555 }
556
video_setpal(int num_colors,int * red,int * green,int * blue)557 void video_setpal(int num_colors, int *red, int *green, int *blue)
558 {
559 video_setpal_true(num_colors, red, green, blue);
560 }
561
562 /*
563 * $Log: video_x.c,v $
564 * Revision 1.26 2000/09/16 16:01:59 nyef
565 * removed some (unused) errno stuff
566 *
567 * Revision 1.25 2000/05/15 01:22:50 nyef
568 * changed to work with new video interface
569 *
570 * Revision 1.24 2000/05/07 00:25:38 nyef
571 * fixed to work with new joypad interface names
572 *
573 * Revision 1.23 2000/03/12 21:43:02 nyef
574 * disabled F1 (debugger) and ESC (quit) keys (they didn't work)
575 *
576 * Revision 1.22 2000/03/06 00:39:33 nyef
577 * added keyboard emulation hook
578 *
579 * Revision 1.21 2000/02/20 22:00:32 nyef
580 * added 24/32 bpp support
581 *
582 * Revision 1.20 2000/02/20 17:12:50 nyef
583 * cleaned up the SHM support a bit
584 *
585 * Revision 1.19 2000/02/20 07:34:50 nyef
586 * cleaned up some of the image buffer manipulation code
587 *
588 * Revision 1.18 2000/01/01 03:22:38 nyef
589 * added preliminary keypad implementation
590 *
591 * Revision 1.17 1999/12/31 16:50:30 nyef
592 * changed how the joypad was implemented, especially wrt X keyboard events
593 *
594 * Revision 1.16 1999/06/05 02:41:39 nyef
595 * converted to use new joypad interface
596 *
597 * Revision 1.15 1999/02/15 03:33:54 nyef
598 * added patch from Michael Vance for XShm
599 *
600 * Revision 1.14 1999/02/13 22:23:51 nyef
601 * added support for the keypad arrow keys
602 *
603 * Revision 1.13 1999/02/07 17:55:51 nyef
604 * removed double-buffering code
605 * added video_setsize() interface
606 *
607 * Revision 1.12 1999/01/05 04:28:41 nyef
608 * hacked up an interface to allow setting the palette.
609 *
610 * Revision 1.11 1998/08/29 22:09:37 nyef
611 * switched to a double-buffered video system. added routines to support
612 * the PPU writing directly into the video buffers. eliminated the 8-bit
613 * vidxlate routine. changed vidxlate16 to operate using only one buffer.
614 * changed the video buffer size to a symbolic constant.
615 *
616 * Revision 1.10 1998/08/29 14:52:29 nyef
617 * added an identity vid_pre_xlat table for use in 16-bitplane modes.
618 *
619 * Revision 1.9 1998/08/28 03:02:16 nyef
620 * added a "vid_pre_xlat" table so that some of the color translation
621 * could be done by the PPU. also moved the 8-bit color translation to
622 * the PPU and set the 8-bit xlatfinc to be memcpy. (maybe I should
623 * double buffer the display?)
624 *
625 * Revision 1.8 1998/08/15 15:45:34 nyef
626 * reworked vidxlate16 to use a normal loop and to copy longs.
627 * this should be slightly faster on a pentium.
628 *
629 * Revision 1.7 1998/08/15 01:08:15 nyef
630 * changed unsupported display depth message to indicate current depth.
631 *
632 * Revision 1.6 1998/08/02 02:03:08 nyef
633 * preliminary GUIfication completed.
634 *
635 * Revision 1.5 1998/08/01 23:12:45 nyef
636 * removed the checks for the symbol "GRAPHICS".
637 * removed two commented-out printf statements in the input handler.
638 *
639 * Revision 1.4 1998/08/01 00:57:13 nyef
640 * moved old palette values into nespal.c
641 *
642 * Revision 1.3 1998/07/26 13:22:55 nyef
643 * changed vidxlate16 tu use pointer notation and duff's device.
644 *
645 * Revision 1.2 1998/07/18 18:54:27 nyef
646 * changed vidxlate16 to copy shorts instead of chars for a massive speed increase
647 * in 16 bitplane mode.
648 *
649 * Revision 1.1 1998/07/11 22:19:25 nyef
650 * Initial revision
651 *
652 */
653