1 /*
2  * GTK front-end/blitter for DarcNES
3  * (c) Michael K Vance, 1999
4  *     mkv102@psu.edu
5  */
6 
7 /* $Id: ui_gtk.c,v 1.10 2000/08/22 02:06:48 nyef Exp $ */
8 
9 #include "ui_gtk.h"
10 #include "menu.h"
11 
12 /* Callbacks galore. */
13 unsigned char* vid_pre_xlat;
14 shutdown_t dn_shutdown;
15 void (* timeslice )( void* );
16 void* timeslice_data;
17 
18 /* Needed for our Gdk blit later on. */
19 GtkWidget* nes_gtk_window;
20 GdkGC* gc;
21 /* Useful for all manners of depth checking, etc. */
22 GdkVisual* visual;
23 GdkColormap* color_map;
24 
25 /* These can have any bit depth. Use image->bpp to get bit depth. */
26 GdkImage* image_1;
27 GdkImage* image_2;
28 GdkImage* cur_image;
29 
30 /* These always carry 8-bit index values. */
31 unsigned char* video_buffer_1;
32 unsigned char* video_buffer_2;
33 unsigned char* cur_video_buffer;
34 
35 /* Our palette for any 16-bit drawing. */
36 unsigned short* translation_buffer_16;
37 
38 /* Video buffer height, width. */
39 int video_buffer_height;
40 int video_buffer_width;
41 
42 /* Our ROM name. */
43 char rom_name[128];
44 
45 /* Joystick data. */
46 struct joypad *ui_joypad;
47 
48 /*
49  * Bootstrap our UI, get our buffers, etc.
50  */
main(int argc,char * argv[])51 int main( int argc, char* argv[] )
52 {
53     extern int nes_psg_quality; /* FIXME: cheap hack, copied from nes_psg.h */
54 
55   translation_buffer_16 = NULL;
56   vid_pre_xlat = NULL;
57 
58   timeslice = NULL;
59   timeslice_data = NULL;
60   nes_psg_quality = 2;
61 
62   image_1 = image_2 = NULL;
63   video_buffer_1 = video_buffer_2 = NULL;
64 
65   /* Get Gtk up and running. */
66   gtk_set_locale();
67   gtk_init( &argc, &argv );
68 
69   nes_gtk_window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
70   gtk_window_set_title( GTK_WINDOW( nes_gtk_window ), "DarcNES" );
71   gtk_widget_set_usize( nes_gtk_window, 256, 240 );
72   gtk_signal_connect( GTK_OBJECT( nes_gtk_window ), "destroy",
73 		      GTK_SIGNAL_FUNC( destroy ), NULL );
74   gtk_signal_connect( GTK_OBJECT( nes_gtk_window ), "key_press_event",
75 		      GTK_SIGNAL_FUNC( key_press ), NULL );
76   gtk_signal_connect( GTK_OBJECT( nes_gtk_window ), "key_release_event",
77 		      GTK_SIGNAL_FUNC( key_release ), NULL );
78   gtk_widget_set_events( nes_gtk_window, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK );
79   gtk_widget_show( nes_gtk_window );
80 
81   visual = gdk_window_get_visual( nes_gtk_window->window );
82   gc = gdk_gc_new( nes_gtk_window->window );
83   color_map = gdk_window_get_colormap( nes_gtk_window->window );
84 
85   if( argc > 1 )
86   {
87       int system_type;
88       rom_file romfile;
89 
90       strncpy( rom_name, argv[1], 127 );
91       romfile = read_romimage(rom_name);
92       system_type = guess_system(romfile);
93       activate_system(system_type, romfile);
94   }
95   else
96   {
97     show_open_dialog( NULL, NULL );
98   }
99 
100   /* Using GTK_PRIORITY_HIGH causes Gtk events to not occur. */
101   gtk_idle_add( (GtkFunction) emulate_timeslice, NULL );
102 
103   gtk_main( );
104 
105   return 0;
106 }
107 
build_menus(GtkWidget * window)108 void build_menus( GtkWidget* window )
109 {
110   GtkWidget* menu_bar;
111   GtkWidget* file_menu;
112   GtkWidget* file_item;
113   GtkWidget* file_open;
114   GtkWidget* file_quit;
115 
116   menu_bar = gtk_menu_bar_new( );
117 
118   file_menu = gtk_menu_new( );
119 
120   file_open = gtk_menu_item_new_with_label( "Open" );
121   gtk_signal_connect_object( GTK_OBJECT( file_open ), "activate",
122 			     GTK_SIGNAL_FUNC( show_open_dialog ), NULL );
123   gtk_menu_append( GTK_MENU( file_menu ), file_open );
124   gtk_widget_show( file_open );
125 
126   file_quit = gtk_menu_item_new_with_label( "Quit" );
127   gtk_signal_connect_object( GTK_OBJECT( file_quit ), "activate",
128 			     GTK_SIGNAL_FUNC( query_quit ), NULL );
129   gtk_menu_append( GTK_MENU( file_menu ), file_quit );
130   gtk_widget_show( file_quit );
131 
132   file_item = gtk_menu_item_new_with_label( "File" );
133   gtk_menu_item_set_submenu( GTK_MENU_ITEM( file_item ), file_menu );
134   gtk_widget_show( file_item );
135 
136   gtk_menu_bar_append( GTK_MENU_BAR( menu_bar ), file_item );
137   gtk_container_add( GTK_CONTAINER( window ), menu_bar );
138   gtk_widget_show( menu_bar );
139 }
140 
show_open_dialog(GtkWidget * w,gpointer data)141 void show_open_dialog( GtkWidget* w, gpointer data )
142 {
143   GtkWidget* open_dialog;
144 
145   open_dialog = gtk_file_selection_new( "Select ROM" );
146   gtk_signal_connect( GTK_OBJECT( GTK_FILE_SELECTION( open_dialog )->ok_button ), "clicked",
147 		      (GtkSignalFunc) file_selected, open_dialog );
148   gtk_signal_connect_object( GTK_OBJECT( GTK_FILE_SELECTION( open_dialog )->cancel_button ),
149 			     "clicked", (GtkSignalFunc) gtk_widget_destroy,
150 			     GTK_OBJECT( open_dialog ) );
151   gtk_widget_show( open_dialog );
152 }
153 
query_quit(GtkWidget * w,gpointer data)154 void query_quit( GtkWidget* w, gpointer data )
155 {
156   /* PENDING( are you sure, etc ) */
157   gtk_exit( 0 );
158 }
159 
file_selected(GtkWidget * w,GtkFileSelection * fs)160 void file_selected( GtkWidget* w, GtkFileSelection* fs )
161 {
162     rom_file romfile;
163     int system_type;
164 
165     strncpy( rom_name, gtk_file_selection_get_filename( GTK_FILE_SELECTION( fs ) ), 127 );
166     gtk_widget_destroy( GTK_WIDGET( fs ) );
167     romfile = read_romimage(rom_name);
168     system_type = guess_system(romfile);
169     activate_system( system_type, romfile);
170 }
171 
destroy(void)172 void destroy( void )
173 {
174 
175   if( dn_shutdown )
176   {
177     dn_shutdown( );
178   }
179 
180   gtk_exit( 0 );
181 }
182 
183 /* per-driver menus */
184 
menu_init(struct ui_menu * menu)185 void menu_init(struct ui_menu *menu)
186 {
187     /* dummy implementation */
188 }
189 
menu_rename_item(struct ui_menu * item)190 void menu_rename_item(struct ui_menu *item)
191 {
192     /* dummy implementation */
193 }
194 
menu_disable_item(struct ui_menu * item)195 void menu_disable_item(struct ui_menu *item)
196 {
197     /* dummy implementation */
198 }
199 
menu_enable_item(struct ui_menu * item)200 void menu_enable_item(struct ui_menu *item)
201 {
202     /* dummy implementation */
203 }
204 
menu_file_open_box(ui_open_callback callback,void * data,char * filter)205 void menu_file_open_box(ui_open_callback callback, void *data, char *filter)
206 {
207     /* dummy implementation */
208 }
209 
210 
deb_printf(const char * format,...)211 void deb_printf( const char* format, ... )
212 {
213   va_list args;
214 
215   va_start( args, format );
216   vprintf( format, args );
217   va_end( args );
218 }
219 
set_timeslice(void (* proc)(void *),void * data)220 void set_timeslice( void (* proc)( void* ), void* data )
221 {
222   timeslice = proc;
223   timeslice_data = data;
224 }
225 
unset_timeslice(void)226 void unset_timeslice( void )
227 {
228   timeslice = NULL;
229   timeslice_data = NULL;
230 }
231 
emulate_timeslice(void)232 gboolean emulate_timeslice( void )
233 {
234 
235   if( timeslice )
236   {
237     timeslice( timeslice_data );
238   }
239 
240   if( system_flags & F_QUIT )
241   {
242     destroy( );
243   }
244 
245   return TRUE;
246 }
247 
video_setsize(int width,int height)248 void video_setsize( int width, int height )
249 {
250   video_buffer_width = width;
251   video_buffer_height = height;
252 
253   if( video_buffer_1 )
254   {
255     free( video_buffer_1 );
256   }
257 
258   if( image_1 )
259   {
260     gdk_image_destroy( image_1 );
261   }
262 
263   if( video_buffer_2 )
264   {
265     free( video_buffer_2 );
266   }
267 
268   if( image_2 )
269   {
270     gdk_image_destroy( image_2 );
271   }
272 
273   gtk_widget_set_usize( GTK_WIDGET( nes_gtk_window ), width, height );
274 
275   /* Buffer allocation. */
276   video_buffer_1 = (unsigned char*) malloc( width * height * 8 );
277   image_1 = gdk_image_new( GDK_IMAGE_FASTEST, visual, width, height );
278   video_buffer_2 = (unsigned char*) malloc( width * height * 8 );
279   image_2 = gdk_image_new( GDK_IMAGE_FASTEST, visual, width, height );
280 
281   cur_image = image_1;
282   cur_video_buffer = video_buffer_1;
283 }
284 
video_setpal(int colors,int * red,int * green,int * blue)285 void video_setpal( int colors, int* red, int* green, int* blue )
286 {
287   int i;
288   GdkColor color;
289 
290   vid_pre_xlat = (unsigned char*) malloc( colors );
291 
292   switch( visual->depth )
293   {
294   case 8:
295 
296     for( i = 0; i < colors; ++i )
297     {
298       color.red = red[i] + ( red[i] << 8 );
299       color.green = green[i] + ( green[i] << 8 );
300       color.blue = blue[i] + ( blue[i] << 8 );
301 
302       if( gdk_colormap_alloc_color( color_map, &color, TRUE, TRUE ) )
303       {
304 	vid_pre_xlat[i] = color.pixel;
305       }
306 
307     }
308 
309     break;
310   case 16:
311     translation_buffer_16 = (unsigned short*) malloc( colors * 2 );
312 
313     for( i = 0; i < colors; ++i )
314     {
315       vid_pre_xlat[i] = i;
316       color.red = red[i] + ( red[i] << 8 );
317       color.green = green[i] + ( green[i] << 8 );
318       color.blue = blue[i] + ( blue[i] << 8 );
319 
320       if( gdk_colormap_alloc_color( color_map, &color, TRUE, TRUE ) )
321       {
322 	translation_buffer_16[i] = color.pixel;
323       }
324 
325     }
326 
327     break;
328   default:
329     fprintf( stderr, "darcnes: unsupported display depth %d in video_set_palette()\n",
330 	     visual->depth );
331     break;
332   }
333 
334 }
335 
336 /*
337  * The video buffer is always indexed--the GdkImage however
338  * can have a different depth.
339  */
video_get_vbp(int line)340 unsigned char* video_get_vbp( int line )
341 {
342   return cur_video_buffer + ( video_buffer_width * line );
343 }
344 
345 /*
346  * Since the NES does everything in eight bit index mode,
347  * we need to translate that indexed framebuffer into our
348  * native format for display.
349  */
video_display_buffer(void)350 void video_display_buffer( void )
351 {
352   int i;
353   unsigned short* buffer_16;
354   int num_pixels;
355 
356   switch( cur_image->bpp )
357   {
358   case 1:
359     /* Empty. */
360     break;
361   case 2:
362     buffer_16 = (unsigned short*) cur_image->mem;
363     num_pixels = video_buffer_width * video_buffer_height;
364 
365     for( i = 0; i < num_pixels; ++i )
366     {
367       buffer_16[i] = translation_buffer_16[ (int) cur_video_buffer[i] ];
368     }
369 
370     break;
371   default:
372     fprintf( stderr, "darcnes: unknown display depth %d in video_display_buffer()\n",
373 	     cur_image->bpp );
374     break;
375   }
376 
377   gdk_draw_image( nes_gtk_window->window, gc, cur_image, 0, 0, 0, 0,
378 		  video_buffer_width, video_buffer_height );
379   gdk_flush( );
380 
381   cur_image = ( cur_image == image_1 ) ? image_2 : image_1;
382   cur_video_buffer = ( cur_video_buffer == video_buffer_1 ) ? video_buffer_2 : video_buffer_1;
383 }
384 
video_enter_deb(void)385 void video_enter_deb( void ) { }
386 
video_leave_deb(void)387 void video_leave_deb( void ) { }
388 
kb_init(void)389 void kb_init(void)
390 {
391     /* FIXME: dummy function. keyboards don't work */
392 }
393 
keypad_register(struct keypad * pad)394 int keypad_register(struct keypad *pad)
395 {
396     /* FIXME: dummy function. keypads don't work */
397     return 0;
398 }
399 
ui_register_joypad(struct joypad * pad)400 int ui_register_joypad(struct joypad *pad)
401 {
402     if (!ui_joypad) {
403 	ui_joypad = pad;
404 	return 1;
405     } else {
406 	return 0;
407     }
408 }
409 
ui_update_joypad(struct joypad * pad)410 void ui_update_joypad(struct joypad *pad)
411 {
412     /* NOTE: does nothing. may want to do stuff when using real gamepads */
413 }
414 
key_press(GtkWidget * w,GdkEventKey * e,gpointer unused)415 gint key_press (GtkWidget* w, GdkEventKey* e, gpointer unused)
416 {
417     switch (e->keyval) {
418     case GDK_Escape:
419 	system_flags |= F_QUIT;
420 	break;
421     case GDK_F1:
422 	system_flags |= F_BREAK;
423 	break;
424     case GDK_S:
425     case GDK_s:
426 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 4)) {
427 	    ui_joypad->data |= ui_joypad->button_template->buttons[4];
428 	}
429 	break;
430     case GDK_A:
431     case GDK_a:
432 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 5)) {
433 	    ui_joypad->data |= ui_joypad->button_template->buttons[5];
434 	}
435 	break;
436     case GDK_bracketleft:
437     case GDK_braceleft:
438 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 6)) {
439 	    ui_joypad->data |= ui_joypad->button_template->buttons[6];
440 	}
441 	break;
442     case GDK_bracketright:
443     case GDK_braceright:
444 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 7)) {
445 	    ui_joypad->data |= ui_joypad->button_template->buttons[7];
446 	}
447 	break;
448     case GDK_Up:
449     case GDK_KP_Up:
450 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 0)) {
451 	    ui_joypad->data |= ui_joypad->button_template->buttons[0];
452 	}
453 	break;
454     case GDK_Down:
455     case GDK_KP_Down:
456 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 1)) {
457 	    ui_joypad->data |= ui_joypad->button_template->buttons[1];
458 	}
459 	break;
460     case GDK_Left:
461     case GDK_KP_Left:
462 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 2)) {
463 	    ui_joypad->data |= ui_joypad->button_template->buttons[2];
464 	}
465 	break;
466     case GDK_Right:
467     case GDK_KP_Right:
468 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 3)) {
469 	    ui_joypad->data |= ui_joypad->button_template->buttons[3];
470 	}
471 	break;
472     }
473 
474     return 0;
475 }
476 
key_release(GtkWidget * w,GdkEventKey * e,gpointer unused)477 gint key_release (GtkWidget* w, GdkEventKey* e, gpointer unused)
478 {
479     switch (e->keyval) {
480     case GDK_S:
481     case GDK_s:
482 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 4)) {
483 	    ui_joypad->data &= ~ui_joypad->button_template->buttons[4];
484 	}
485 	break;
486     case GDK_A:
487     case GDK_a:
488 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 5)) {
489 	    ui_joypad->data &= ~ui_joypad->button_template->buttons[5];
490 	}
491 	break;
492     case GDK_bracketleft:
493     case GDK_braceleft:
494 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 6)) {
495 	    ui_joypad->data &= ~ui_joypad->button_template->buttons[6];
496 	}
497 	break;
498     case GDK_bracketright:
499     case GDK_braceright:
500 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 7)) {
501 	    ui_joypad->data &= ~ui_joypad->button_template->buttons[7];
502 	}
503 	break;
504     case GDK_Up:
505     case GDK_KP_Up:
506 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 0)) {
507 	    ui_joypad->data &= ~ui_joypad->button_template->buttons[0];
508 	}
509 	break;
510     case GDK_Down:
511     case GDK_KP_Down:
512 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 1)) {
513 	    ui_joypad->data &= ~ui_joypad->button_template->buttons[1];
514 	}
515 	break;
516     case GDK_Left:
517     case GDK_KP_Left:
518 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 2)) {
519 	    ui_joypad->data &= ~ui_joypad->button_template->buttons[2];
520 	}
521 	break;
522     case GDK_Right:
523     case GDK_KP_Right:
524 	if (ui_joypad && (ui_joypad->button_template->num_buttons > 3)) {
525 	    ui_joypad->data &= ~ui_joypad->button_template->buttons[3];
526 	}
527 	break;
528     }
529 
530     return 0;
531 }
532 
533 /*
534  * $Log: ui_gtk.c,v $
535  * Revision 1.10  2000/08/22 02:06:48  nyef
536  * added dummy implementation of menu_file_open_box()
537  *
538  * Revision 1.9  2000/07/04 23:19:41  nyef
539  * fixed compile problems with the dummy menu implementation
540  *
541  * Revision 1.8  2000/07/02 02:43:18  nyef
542  * added dummy implementation of the per-driver menu interface
543  *
544  * Revision 1.7  2000/06/03 17:25:14  nyef
545  * fixed (hopefully) to work with the new joypad interface
546  * added dummy functions for keypad and keyboard emulation
547  *
548  * Revision 1.6  2000/05/31 01:22:39  nyef
549  * fixed (hopefully) to work with the new video interface
550  *
551  * Revision 1.5  1999/11/26 20:08:21  nyef
552  * moved sound quality control in from the game system layer
553  *
554  * Revision 1.4  1999/11/21 03:09:49  nyef
555  * converted to use new rom file interface (not tested)
556  *
557  * Revision 1.3  1999/06/14 15:33:04  nyef
558  * converted to use new joypad interface
559  *
560  * Revision 1.2  1999/04/17 20:11:49  nyef
561  * changed shutdown() to dn_shutdown().
562  *
563  * Revision 1.1  1999/02/15 03:39:46  nyef
564  * Initial revision
565  *
566  */
567