ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/Display_SDL.h
Revision: 1.12
Committed: 2010-04-23T22:02:35Z (13 years, 11 months ago) by cebix
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.11: +6 -13 lines
Log Message:
optimized the shader

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * Display_SDL.h - C64 graphics display, emulator window handling,
3     * SDL specific stuff
4     *
5 cebix 1.9 * Frodo Copyright (C) Christian Bauer
6 cebix 1.1 *
7     * This program is free software; you can redistribute it and/or modify
8     * it under the terms of the GNU General Public License as published by
9     * the Free Software Foundation; either version 2 of the License, or
10     * (at your option) any later version.
11     *
12     * This program is distributed in the hope that it will be useful,
13     * but WITHOUT ANY WARRANTY; without even the implied warranty of
14     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     * GNU General Public License for more details.
16     *
17     * You should have received a copy of the GNU General Public License
18     * along with this program; if not, write to the Free Software
19     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20     */
21    
22     #include "C64.h"
23     #include "SAM.h"
24     #include "Version.h"
25    
26     #include <SDL.h>
27    
28 cebix 1.11 #ifdef ENABLE_OPENGL
29     #include <GL/glew.h>
30     #endif
31    
32    
33     // Display dimensions including drive LEDs etc.
34     static const int FRAME_WIDTH = DISPLAY_X;
35     static const int FRAME_HEIGHT = DISPLAY_Y + 16;
36 cebix 1.1
37     // Display surface
38     static SDL_Surface *screen = NULL;
39    
40     // Keyboard
41     static bool num_locked = false;
42    
43     // For LED error blinking
44     static C64Display *c64_disp;
45     static struct sigaction pulse_sa;
46     static itimerval pulse_tv;
47    
48 cebix 1.4 // SDL joysticks
49     static SDL_Joystick *joy[2] = {NULL, NULL};
50    
51 cebix 1.1 // Colors for speedometer/drive LEDs
52     enum {
53     black = 0,
54     white = 1,
55     fill_gray = 16,
56     shine_gray = 17,
57     shadow_gray = 18,
58     red = 19,
59     green = 20,
60     PALETTE_SIZE = 21
61     };
62    
63 cebix 1.11 #ifdef ENABLE_OPENGL
64    
65     // Display texture dimensions
66     static const int TEXTURE_SIZE = 512; // smallest power-of-two that fits DISPLAY_X/Y
67    
68     // Texture object for VIC palette
69     static GLuint palette_tex;
70    
71     // Texture object for VIC display
72     static GLuint vic_tex;
73    
74     #endif
75    
76 cebix 1.1 /*
77     C64 keyboard matrix:
78    
79     Bit 7 6 5 4 3 2 1 0
80     0 CUD F5 F3 F1 F7 CLR RET DEL
81     1 SHL E S Z 4 A W 3
82     2 X T F C 6 D R 5
83     3 V U H B 8 G Y 7
84     4 N O K M 0 J I 9
85     5 , @ : . - L P +
86     6 / ^ = SHR HOM ; * £
87     7 R/S Q C= SPC 2 CTL <- 1
88     */
89    
90     #define MATRIX(a,b) (((a) << 3) | (b))
91    
92    
93     /*
94     * Open window
95     */
96    
97     int init_graphics(void)
98     {
99     // Init SDL
100 cebix 1.10 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO) < 0) {
101 cebix 1.1 fprintf(stderr, "Couldn't initialize SDL (%s)\n", SDL_GetError());
102     return 0;
103     }
104    
105     return 1;
106     }
107    
108    
109 cebix 1.11 #ifdef ENABLE_OPENGL
110     /*
111     * Set direct projection (GL coordinates = window pixel coordinates)
112     */
113    
114     static void set_projection()
115     {
116     int width = SDL_GetVideoSurface()->w;
117     int height = SDL_GetVideoSurface()->h;
118    
119     glViewport(0, 0, width, height);
120     glMatrixMode(GL_PROJECTION);
121     glLoadIdentity();
122    
123     float aspect = float(width) / float(height);
124     const float want_aspect = float(FRAME_WIDTH) / float(FRAME_HEIGHT);
125     int left, right, top, bottom;
126     if (aspect > want_aspect) {
127     // Window too wide, center horizontally
128     top = 0; bottom = FRAME_HEIGHT;
129     int diff = (int(FRAME_WIDTH * aspect / want_aspect) - FRAME_WIDTH) / 2;
130     left = -diff;
131     right = FRAME_WIDTH + diff;
132     } else {
133     // Window too high, center vertically
134     left = 0; right = FRAME_WIDTH;
135     int diff = (int(FRAME_HEIGHT * want_aspect / aspect) - FRAME_HEIGHT) / 2;
136     top = -diff;
137     bottom = FRAME_HEIGHT + diff;
138     }
139     glOrtho(left, right, bottom, top, -1, 1);
140    
141     glClear(GL_COLOR_BUFFER_BIT);
142     }
143    
144    
145     /*
146     * User resized video display (only possible with OpenGL)
147     */
148    
149     static void video_resized(int width, int height)
150     {
151     uint32 flags = (ThePrefs.DisplayType == DISPTYPE_SCREEN ? SDL_FULLSCREEN : 0);
152     flags |= (SDL_ANYFORMAT | SDL_OPENGL | SDL_RESIZABLE);
153     SDL_SetVideoMode(width, height, 16, flags);
154     set_projection();
155     }
156     #endif
157    
158    
159 cebix 1.1 /*
160     * Display constructor
161     */
162    
163     C64Display::C64Display(C64 *the_c64) : TheC64(the_c64)
164     {
165     quit_requested = false;
166     speedometer_string[0] = 0;
167 cebix 1.5
168     // Open window
169     SDL_WM_SetCaption(VERSION_STRING, "Frodo");
170 cebix 1.11 uint32 flags = (ThePrefs.DisplayType == DISPTYPE_SCREEN ? SDL_FULLSCREEN : 0);
171    
172     #ifdef ENABLE_OPENGL
173    
174     flags |= (SDL_ANYFORMAT | SDL_OPENGL | SDL_RESIZABLE);
175     SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
176     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
177     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
178     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
179     SDL_Surface *real_screen = SDL_SetVideoMode(FRAME_WIDTH * 2, FRAME_HEIGHT * 2, 16, flags);
180     if (!real_screen) {
181     fprintf(stderr, "Couldn't initialize OpenGL video output (%s)\n", SDL_GetError());
182     exit(1);
183     }
184    
185     // VIC display and UI elements are rendered into an off-screen surface
186     screen = SDL_CreateRGBSurface(SDL_SWSURFACE, FRAME_WIDTH, FRAME_HEIGHT, 8, 0xff, 0xff, 0xff, 0xff);
187    
188     // We need OpenGL 2.0 or higher
189     GLenum err = glewInit();
190     if (err != GLEW_OK) {
191     fprintf(stderr, "Couldn't initialize GLEW (%s)\n", glewGetErrorString(err));
192     exit(1);
193     }
194    
195     if (!glewIsSupported("GL_VERSION_2_0")) {
196     fprintf(stderr, "Frodo requires OpenGL 2.0 or higher\n");
197     exit(1);
198     }
199    
200     // Set direct projection
201     set_projection();
202    
203     // Set GL state
204     glShadeModel(GL_FLAT);
205     glDisable(GL_DITHER);
206     glColor4f(1.0, 1.0, 1.0, 1.0);
207    
208     // Create fragment shader for emulating a paletted texture
209     GLuint shader = glCreateShader(GL_FRAGMENT_SHADER_ARB);
210     const char * src =
211     "uniform sampler2D screen;"
212     "uniform sampler1D palette;"
213 cebix 1.12 "const float texSize = 512.0;" // texture size
214     "const float texel = 1.0 / texSize;"
215 cebix 1.11 "void main()"
216     "{"
217     #if 0
218     // Nearest neighbour
219     " vec4 idx = texture2D(screen, gl_TexCoord[0].st);"
220     " gl_FragColor = texture1D(palette, idx.r);"
221     #else
222     // Linear interpolation
223     // (setting the GL_TEXTURE_MAG_FILTER to GL_LINEAR would interpolate
224     // the color indices which is not what we want; we need to manually
225     // interpolate the palette values instead)
226     " vec2 st = gl_TexCoord[0].st - vec2(texel * 0.5, texel * 0.5);"
227     " vec4 idx00 = texture2D(screen, st);"
228     " vec4 idx01 = texture2D(screen, st + vec2(0, texel));"
229     " vec4 idx10 = texture2D(screen, st + vec2(texel, 0));"
230     " vec4 idx11 = texture2D(screen, st + vec2(texel, texel));"
231 cebix 1.12 " vec2 f = fract(st * texSize);"
232     " vec4 color0 = mix(texture1D(palette, idx00.r), texture1D(palette, idx01.r), f.y);"
233     " vec4 color1 = mix(texture1D(palette, idx10.r), texture1D(palette, idx11.r), f.y);"
234     " gl_FragColor = mix(color0, color1, f.x);"
235 cebix 1.11 #endif
236     "}";
237     glShaderSource(shader, 1, &src, NULL);
238     glCompileShader(shader);
239    
240     GLint status;
241     glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
242     if (status == GL_FALSE) {
243     GLint logLength = 0;
244     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
245     if (logLength > 0) {
246     GLchar *log = (GLchar *)malloc(logLength);
247     GLint actual;
248     glGetShaderInfoLog(shader, logLength, &actual, log);
249     fprintf(stderr, "%s\n", log);
250     exit(1);
251     }
252     }
253    
254     GLuint program = glCreateProgram();
255     glAttachShader(program, shader);
256     glLinkProgram(program);
257     glUseProgram(program);
258    
259     // Create VIC display texture (8-bit color index in the red channel)
260     uint8 *tmp = (uint8 *)malloc(TEXTURE_SIZE * TEXTURE_SIZE);
261     memset(tmp, 0, TEXTURE_SIZE * TEXTURE_SIZE);
262    
263     glGenTextures(1, &vic_tex);
264     glActiveTexture(GL_TEXTURE0);
265     glBindTexture(GL_TEXTURE_2D, vic_tex);
266     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
267     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
268     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // don't interpolate color index values
269     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
270     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_SIZE, TEXTURE_SIZE, 0, GL_RED, GL_UNSIGNED_BYTE, tmp);
271     glUniform1i(glGetUniformLocation(program, "screen"), 0);
272    
273     free(tmp);
274    
275     // Create VIC palette texture
276     tmp = (uint8 *)malloc(256 * 3);
277     memset(tmp, 0xff, 256 * 3);
278     for (int i=0; i<16; ++i) {
279     tmp[i*3+0] = palette_red[i & 0x0f];
280     tmp[i*3+1] = palette_green[i & 0x0f];
281     tmp[i*3+2] = palette_blue[i & 0x0f];
282     }
283     tmp[fill_gray*3+0] = tmp[fill_gray*3+1] = tmp[fill_gray*3+2] = 0xd0;
284     tmp[shine_gray*3+0] = tmp[shine_gray*3+1] = tmp[shine_gray*3+2] = 0xf0;
285     tmp[shadow_gray*3+0] = tmp[shadow_gray*3+1] = tmp[shadow_gray*3+2] = 0x80;
286     tmp[red*3+0] = 0xf0;
287     tmp[red*3+1] = tmp[red*3+2] = 0;
288     tmp[green*3+1] = 0xf0;
289     tmp[green*3+0] = tmp[green*3+2] = 0;
290    
291     glGenTextures(1, &palette_tex);
292     glActiveTexture(GL_TEXTURE1);
293     glBindTexture(GL_TEXTURE_1D, palette_tex);
294     glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // don't interpolate palette entries
295     glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
296     glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA8, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, tmp);
297     glUniform1i(glGetUniformLocation(program, "palette"), 1);
298    
299     free(tmp);
300    
301     #else
302    
303     flags |= (SDL_HWSURFACE | SDL_DOUBLEBUF);
304     screen = SDL_SetVideoMode(FRAME_WIDTH, FRAME_HEIGHT, 8, flags);
305    
306     #endif
307 cebix 1.1
308 cebix 1.9 // Hide mouse pointer in fullscreen mode
309     if (ThePrefs.DisplayType == DISPTYPE_SCREEN)
310     SDL_ShowCursor(0);
311    
312 cebix 1.1 // LEDs off
313     for (int i=0; i<4; i++)
314     led_state[i] = old_led_state[i] = LED_OFF;
315    
316     // Start timer for LED error blinking
317     c64_disp = this;
318     pulse_sa.sa_handler = (void (*)(int))pulse_handler;
319 cebix 1.3 pulse_sa.sa_flags = SA_RESTART;
320 cebix 1.1 sigemptyset(&pulse_sa.sa_mask);
321     sigaction(SIGALRM, &pulse_sa, NULL);
322     pulse_tv.it_interval.tv_sec = 0;
323     pulse_tv.it_interval.tv_usec = 400000;
324     pulse_tv.it_value.tv_sec = 0;
325     pulse_tv.it_value.tv_usec = 400000;
326     setitimer(ITIMER_REAL, &pulse_tv, NULL);
327     }
328    
329    
330     /*
331     * Display destructor
332     */
333    
334     C64Display::~C64Display()
335     {
336 cebix 1.9 pulse_tv.it_interval.tv_sec = 0;
337     pulse_tv.it_interval.tv_usec = 0;
338     pulse_tv.it_value.tv_sec = 0;
339     pulse_tv.it_value.tv_usec = 0;
340     setitimer(ITIMER_REAL, &pulse_tv, NULL);
341    
342 cebix 1.1 SDL_Quit();
343 cebix 1.9
344     c64_disp = NULL;
345 cebix 1.1 }
346    
347    
348     /*
349     * Prefs may have changed
350     */
351    
352     void C64Display::NewPrefs(Prefs *prefs)
353     {
354 cebix 1.11 // Unused, we handle fullscreen/window mode switches in PollKeyboard()
355 cebix 1.1 }
356    
357    
358     /*
359     * Redraw bitmap
360     */
361    
362     void C64Display::Update(void)
363     {
364     // Draw speedometer/LEDs
365     SDL_Rect r = {0, DISPLAY_Y, DISPLAY_X, 15};
366     SDL_FillRect(screen, &r, fill_gray);
367     r.w = DISPLAY_X; r.h = 1;
368     SDL_FillRect(screen, &r, shine_gray);
369     r.y = DISPLAY_Y + 14;
370     SDL_FillRect(screen, &r, shadow_gray);
371     r.w = 16;
372     for (int i=2; i<6; i++) {
373     r.x = DISPLAY_X * i/5 - 24; r.y = DISPLAY_Y + 4;
374     SDL_FillRect(screen, &r, shadow_gray);
375     r.y = DISPLAY_Y + 10;
376     SDL_FillRect(screen, &r, shine_gray);
377     }
378     r.y = DISPLAY_Y; r.w = 1; r.h = 15;
379     for (int i=0; i<5; i++) {
380     r.x = DISPLAY_X * i / 5;
381     SDL_FillRect(screen, &r, shine_gray);
382     r.x = DISPLAY_X * (i+1) / 5 - 1;
383     SDL_FillRect(screen, &r, shadow_gray);
384     }
385     r.y = DISPLAY_Y + 4; r.h = 7;
386     for (int i=2; i<6; i++) {
387     r.x = DISPLAY_X * i/5 - 24;
388     SDL_FillRect(screen, &r, shadow_gray);
389     r.x = DISPLAY_X * i/5 - 9;
390     SDL_FillRect(screen, &r, shine_gray);
391     }
392     r.y = DISPLAY_Y + 5; r.w = 14; r.h = 5;
393     for (int i=0; i<4; i++) {
394     r.x = DISPLAY_X * (i+2) / 5 - 23;
395     int c;
396     switch (led_state[i]) {
397     case LED_ON:
398     c = green;
399     break;
400     case LED_ERROR_ON:
401     c = red;
402     break;
403     default:
404     c = black;
405     break;
406     }
407     SDL_FillRect(screen, &r, c);
408     }
409    
410     draw_string(screen, DISPLAY_X * 1/5 + 8, DISPLAY_Y + 4, "D\x12 8", black, fill_gray);
411     draw_string(screen, DISPLAY_X * 2/5 + 8, DISPLAY_Y + 4, "D\x12 9", black, fill_gray);
412     draw_string(screen, DISPLAY_X * 3/5 + 8, DISPLAY_Y + 4, "D\x12 10", black, fill_gray);
413     draw_string(screen, DISPLAY_X * 4/5 + 8, DISPLAY_Y + 4, "D\x12 11", black, fill_gray);
414     draw_string(screen, 24, DISPLAY_Y + 4, speedometer_string, black, fill_gray);
415    
416 cebix 1.11 #ifdef ENABLE_OPENGL
417     // Load screen to texture
418     glActiveTexture(GL_TEXTURE0);
419     glBindTexture(GL_TEXTURE_2D, vic_tex);
420     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, FRAME_WIDTH, FRAME_HEIGHT, GL_RED, GL_UNSIGNED_BYTE, screen->pixels);
421    
422     // Draw textured rectangle
423     glBegin(GL_QUADS);
424     glTexCoord2f(0.0, 0.0);
425     glVertex2f(0.0, 0.0);
426     glTexCoord2f(float(FRAME_WIDTH) / TEXTURE_SIZE, 0.0);
427     glVertex2f(float(FRAME_WIDTH), 0.0);
428     glTexCoord2f(float(FRAME_WIDTH) / TEXTURE_SIZE, float(FRAME_HEIGHT) / TEXTURE_SIZE);
429     glVertex2f(float(FRAME_WIDTH), float(FRAME_HEIGHT));
430     glTexCoord2f(0.0, float(FRAME_HEIGHT) / TEXTURE_SIZE);
431     glVertex2f(0.0, float(FRAME_HEIGHT));
432     glEnd();
433    
434     // Update display
435     SDL_GL_SwapBuffers();
436     #else
437 cebix 1.1 // Update display
438     SDL_Flip(screen);
439 cebix 1.11 #endif
440 cebix 1.1 }
441    
442    
443     /*
444     * Draw string into surface using the C64 ROM font
445     */
446    
447     void C64Display::draw_string(SDL_Surface *s, int x, int y, const char *str, uint8 front_color, uint8 back_color)
448     {
449     uint8 *pb = (uint8 *)s->pixels + s->pitch*y + x;
450     char c;
451     while ((c = *str++) != 0) {
452     uint8 *q = TheC64->Char + c*8 + 0x800;
453     uint8 *p = pb;
454     for (int y=0; y<8; y++) {
455     uint8 v = *q++;
456     p[0] = (v & 0x80) ? front_color : back_color;
457     p[1] = (v & 0x40) ? front_color : back_color;
458     p[2] = (v & 0x20) ? front_color : back_color;
459     p[3] = (v & 0x10) ? front_color : back_color;
460     p[4] = (v & 0x08) ? front_color : back_color;
461     p[5] = (v & 0x04) ? front_color : back_color;
462     p[6] = (v & 0x02) ? front_color : back_color;
463     p[7] = (v & 0x01) ? front_color : back_color;
464     p += s->pitch;
465     }
466     pb += 8;
467     }
468     }
469    
470    
471     /*
472     * LED error blink
473     */
474    
475     void C64Display::pulse_handler(...)
476     {
477     for (int i=0; i<4; i++)
478     switch (c64_disp->led_state[i]) {
479     case LED_ERROR_ON:
480     c64_disp->led_state[i] = LED_ERROR_OFF;
481     break;
482     case LED_ERROR_OFF:
483     c64_disp->led_state[i] = LED_ERROR_ON;
484     break;
485     }
486     }
487    
488    
489     /*
490     * Draw speedometer
491     */
492    
493     void C64Display::Speedometer(int speed)
494     {
495     static int delay = 0;
496    
497     if (delay >= 20) {
498     delay = 0;
499     sprintf(speedometer_string, "%d%%", speed);
500     } else
501     delay++;
502     }
503    
504    
505     /*
506     * Return pointer to bitmap data
507     */
508    
509     uint8 *C64Display::BitmapBase(void)
510     {
511     return (uint8 *)screen->pixels;
512     }
513    
514    
515     /*
516     * Return number of bytes per row
517     */
518    
519     int C64Display::BitmapXMod(void)
520     {
521     return screen->pitch;
522     }
523    
524    
525     /*
526     * Poll the keyboard
527     */
528    
529     static void translate_key(SDLKey key, bool key_up, uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick)
530     {
531     int c64_key = -1;
532     switch (key) {
533     case SDLK_a: c64_key = MATRIX(1,2); break;
534     case SDLK_b: c64_key = MATRIX(3,4); break;
535     case SDLK_c: c64_key = MATRIX(2,4); break;
536     case SDLK_d: c64_key = MATRIX(2,2); break;
537     case SDLK_e: c64_key = MATRIX(1,6); break;
538     case SDLK_f: c64_key = MATRIX(2,5); break;
539     case SDLK_g: c64_key = MATRIX(3,2); break;
540     case SDLK_h: c64_key = MATRIX(3,5); break;
541     case SDLK_i: c64_key = MATRIX(4,1); break;
542     case SDLK_j: c64_key = MATRIX(4,2); break;
543     case SDLK_k: c64_key = MATRIX(4,5); break;
544     case SDLK_l: c64_key = MATRIX(5,2); break;
545     case SDLK_m: c64_key = MATRIX(4,4); break;
546     case SDLK_n: c64_key = MATRIX(4,7); break;
547     case SDLK_o: c64_key = MATRIX(4,6); break;
548     case SDLK_p: c64_key = MATRIX(5,1); break;
549     case SDLK_q: c64_key = MATRIX(7,6); break;
550     case SDLK_r: c64_key = MATRIX(2,1); break;
551     case SDLK_s: c64_key = MATRIX(1,5); break;
552     case SDLK_t: c64_key = MATRIX(2,6); break;
553     case SDLK_u: c64_key = MATRIX(3,6); break;
554     case SDLK_v: c64_key = MATRIX(3,7); break;
555     case SDLK_w: c64_key = MATRIX(1,1); break;
556     case SDLK_x: c64_key = MATRIX(2,7); break;
557     case SDLK_y: c64_key = MATRIX(3,1); break;
558     case SDLK_z: c64_key = MATRIX(1,4); break;
559    
560     case SDLK_0: c64_key = MATRIX(4,3); break;
561     case SDLK_1: c64_key = MATRIX(7,0); break;
562     case SDLK_2: c64_key = MATRIX(7,3); break;
563     case SDLK_3: c64_key = MATRIX(1,0); break;
564     case SDLK_4: c64_key = MATRIX(1,3); break;
565     case SDLK_5: c64_key = MATRIX(2,0); break;
566     case SDLK_6: c64_key = MATRIX(2,3); break;
567     case SDLK_7: c64_key = MATRIX(3,0); break;
568     case SDLK_8: c64_key = MATRIX(3,3); break;
569     case SDLK_9: c64_key = MATRIX(4,0); break;
570    
571     case SDLK_SPACE: c64_key = MATRIX(7,4); break;
572     case SDLK_BACKQUOTE: c64_key = MATRIX(7,1); break;
573     case SDLK_BACKSLASH: c64_key = MATRIX(6,6); break;
574     case SDLK_COMMA: c64_key = MATRIX(5,7); break;
575     case SDLK_PERIOD: c64_key = MATRIX(5,4); break;
576     case SDLK_MINUS: c64_key = MATRIX(5,0); break;
577     case SDLK_EQUALS: c64_key = MATRIX(5,3); break;
578     case SDLK_LEFTBRACKET: c64_key = MATRIX(5,6); break;
579     case SDLK_RIGHTBRACKET: c64_key = MATRIX(6,1); break;
580     case SDLK_SEMICOLON: c64_key = MATRIX(5,5); break;
581     case SDLK_QUOTE: c64_key = MATRIX(6,2); break;
582     case SDLK_SLASH: c64_key = MATRIX(6,7); break;
583    
584     case SDLK_ESCAPE: c64_key = MATRIX(7,7); break;
585     case SDLK_RETURN: c64_key = MATRIX(0,1); break;
586     case SDLK_BACKSPACE: case SDLK_DELETE: c64_key = MATRIX(0,0); break;
587     case SDLK_INSERT: c64_key = MATRIX(6,3); break;
588     case SDLK_HOME: c64_key = MATRIX(6,3); break;
589     case SDLK_END: c64_key = MATRIX(6,0); break;
590     case SDLK_PAGEUP: c64_key = MATRIX(6,0); break;
591     case SDLK_PAGEDOWN: c64_key = MATRIX(6,5); break;
592    
593     case SDLK_LCTRL: case SDLK_TAB: c64_key = MATRIX(7,2); break;
594     case SDLK_RCTRL: c64_key = MATRIX(7,5); break;
595     case SDLK_LSHIFT: c64_key = MATRIX(1,7); break;
596     case SDLK_RSHIFT: c64_key = MATRIX(6,4); break;
597     case SDLK_LALT: case SDLK_LMETA: c64_key = MATRIX(7,5); break;
598     case SDLK_RALT: case SDLK_RMETA: c64_key = MATRIX(7,5); break;
599    
600     case SDLK_UP: c64_key = MATRIX(0,7)| 0x80; break;
601     case SDLK_DOWN: c64_key = MATRIX(0,7); break;
602     case SDLK_LEFT: c64_key = MATRIX(0,2) | 0x80; break;
603     case SDLK_RIGHT: c64_key = MATRIX(0,2); break;
604    
605     case SDLK_F1: c64_key = MATRIX(0,4); break;
606     case SDLK_F2: c64_key = MATRIX(0,4) | 0x80; break;
607     case SDLK_F3: c64_key = MATRIX(0,5); break;
608     case SDLK_F4: c64_key = MATRIX(0,5) | 0x80; break;
609     case SDLK_F5: c64_key = MATRIX(0,6); break;
610     case SDLK_F6: c64_key = MATRIX(0,6) | 0x80; break;
611     case SDLK_F7: c64_key = MATRIX(0,3); break;
612     case SDLK_F8: c64_key = MATRIX(0,3) | 0x80; break;
613    
614     case SDLK_KP0: case SDLK_KP5: c64_key = 0x10 | 0x40; break;
615     case SDLK_KP1: c64_key = 0x06 | 0x40; break;
616     case SDLK_KP2: c64_key = 0x02 | 0x40; break;
617     case SDLK_KP3: c64_key = 0x0a | 0x40; break;
618     case SDLK_KP4: c64_key = 0x04 | 0x40; break;
619     case SDLK_KP6: c64_key = 0x08 | 0x40; break;
620     case SDLK_KP7: c64_key = 0x05 | 0x40; break;
621     case SDLK_KP8: c64_key = 0x01 | 0x40; break;
622     case SDLK_KP9: c64_key = 0x09 | 0x40; break;
623    
624     case SDLK_KP_DIVIDE: c64_key = MATRIX(6,7); break;
625     case SDLK_KP_ENTER: c64_key = MATRIX(0,1); break;
626     default: break;
627     }
628    
629     if (c64_key < 0)
630     return;
631    
632     // Handle joystick emulation
633     if (c64_key & 0x40) {
634     c64_key &= 0x1f;
635     if (key_up)
636     *joystick |= c64_key;
637     else
638     *joystick &= ~c64_key;
639     return;
640     }
641    
642     // Handle other keys
643     bool shifted = c64_key & 0x80;
644     int c64_byte = (c64_key >> 3) & 7;
645     int c64_bit = c64_key & 7;
646     if (key_up) {
647     if (shifted) {
648     key_matrix[6] |= 0x10;
649     rev_matrix[4] |= 0x40;
650     }
651     key_matrix[c64_byte] |= (1 << c64_bit);
652     rev_matrix[c64_bit] |= (1 << c64_byte);
653     } else {
654     if (shifted) {
655     key_matrix[6] &= 0xef;
656     rev_matrix[4] &= 0xbf;
657     }
658     key_matrix[c64_byte] &= ~(1 << c64_bit);
659     rev_matrix[c64_bit] &= ~(1 << c64_byte);
660     }
661     }
662    
663     void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick)
664     {
665     SDL_Event event;
666     while (SDL_PollEvent(&event)) {
667     switch (event.type) {
668    
669     // Key pressed
670     case SDL_KEYDOWN:
671     switch (event.key.keysym.sym) {
672    
673     case SDLK_F9: // F9: Invoke SAM
674 cebix 1.9 if (ThePrefs.DisplayType == DISPTYPE_WINDOW) // don't invoke in fullscreen mode
675     SAM(TheC64);
676 cebix 1.1 break;
677    
678 cebix 1.9 case SDLK_F10: // F10: Prefs/Quit
679     TheC64->Pause();
680     if (ThePrefs.DisplayType == DISPTYPE_SCREEN) { // exit fullscreen mode
681 cebix 1.11 SDL_WM_ToggleFullScreen(SDL_GetVideoSurface());
682 cebix 1.9 SDL_ShowCursor(1);
683     }
684    
685     if (!TheApp->RunPrefsEditor()) {
686     quit_requested = true;
687 cebix 1.11 } else {
688     if (ThePrefs.DisplayType == DISPTYPE_SCREEN) { // enter fullscreen mode
689     SDL_ShowCursor(0);
690     SDL_WM_ToggleFullScreen(SDL_GetVideoSurface());
691     }
692 cebix 1.9 }
693    
694     TheC64->Resume();
695 cebix 1.1 break;
696    
697     case SDLK_F11: // F11: NMI (Restore)
698     TheC64->NMI();
699     break;
700    
701     case SDLK_F12: // F12: Reset
702     TheC64->Reset();
703     break;
704    
705     case SDLK_NUMLOCK:
706     num_locked = true;
707     break;
708    
709     case SDLK_KP_PLUS: // '+' on keypad: Increase SkipFrames
710     ThePrefs.SkipFrames++;
711     break;
712    
713     case SDLK_KP_MINUS: // '-' on keypad: Decrease SkipFrames
714     if (ThePrefs.SkipFrames > 1)
715     ThePrefs.SkipFrames--;
716     break;
717    
718     case SDLK_KP_MULTIPLY: // '*' on keypad: Toggle speed limiter
719     ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed;
720     break;
721    
722     default:
723     translate_key(event.key.keysym.sym, false, key_matrix, rev_matrix, joystick);
724     break;
725     }
726     break;
727    
728     // Key released
729     case SDL_KEYUP:
730     if (event.key.keysym.sym == SDLK_NUMLOCK)
731     num_locked = false;
732     else
733     translate_key(event.key.keysym.sym, true, key_matrix, rev_matrix, joystick);
734     break;
735    
736 cebix 1.11 #ifdef ENABLE_OPENGL
737     // Window resized
738     case SDL_VIDEORESIZE:
739     video_resized(event.resize.w, event.resize.h);
740     break;
741     #endif
742    
743 cebix 1.1 // Quit Frodo
744     case SDL_QUIT:
745     quit_requested = true;
746     break;
747     }
748     }
749     }
750    
751    
752     /*
753     * Check if NumLock is down (for switching the joystick keyboard emulation)
754     */
755    
756     bool C64Display::NumLock(void)
757     {
758     return num_locked;
759 cebix 1.4 }
760    
761    
762     /*
763     * Open/close joystick drivers given old and new state of
764     * joystick preferences
765     */
766    
767     void C64::open_close_joystick(int port, int oldjoy, int newjoy)
768     {
769     if (oldjoy != newjoy) {
770     joy_minx[port] = joy_miny[port] = 32767; // Reset calibration
771     joy_maxx[port] = joy_maxy[port] = -32768;
772     if (newjoy) {
773     joy[port] = SDL_JoystickOpen(newjoy - 1);
774     if (joy[port] == NULL)
775     fprintf(stderr, "Couldn't open joystick %d\n", port + 1);
776     } else {
777     if (joy[port]) {
778     SDL_JoystickClose(joy[port]);
779     joy[port] = NULL;
780     }
781     }
782     }
783     }
784    
785     void C64::open_close_joysticks(int oldjoy1, int oldjoy2, int newjoy1, int newjoy2)
786     {
787     open_close_joystick(0, oldjoy1, newjoy1);
788     open_close_joystick(1, oldjoy2, newjoy2);
789     }
790    
791    
792     /*
793     * Poll joystick port, return CIA mask
794     */
795    
796     uint8 C64::poll_joystick(int port)
797     {
798     uint8 j = 0xff;
799    
800     if (port == 0 && (joy[0] || joy[1]))
801     SDL_JoystickUpdate();
802    
803     if (joy[port]) {
804     int x = SDL_JoystickGetAxis(joy[port], 0), y = SDL_JoystickGetAxis(joy[port], 1);
805    
806     if (x > joy_maxx[port])
807     joy_maxx[port] = x;
808     if (x < joy_minx[port])
809     joy_minx[port] = x;
810     if (y > joy_maxy[port])
811     joy_maxy[port] = y;
812     if (y < joy_miny[port])
813     joy_miny[port] = y;
814    
815     if (joy_maxx[port] - joy_minx[port] < 100 || joy_maxy[port] - joy_miny[port] < 100)
816     return 0xff;
817    
818     if (x < (joy_minx[port] + (joy_maxx[port]-joy_minx[port])/3))
819     j &= 0xfb; // Left
820     else if (x > (joy_minx[port] + 2*(joy_maxx[port]-joy_minx[port])/3))
821     j &= 0xf7; // Right
822    
823     if (y < (joy_miny[port] + (joy_maxy[port]-joy_miny[port])/3))
824     j &= 0xfe; // Up
825     else if (y > (joy_miny[port] + 2*(joy_maxy[port]-joy_miny[port])/3))
826     j &= 0xfd; // Down
827    
828     if (SDL_JoystickGetButton(joy[port], 0))
829     j &= 0xef; // Button
830     }
831    
832     return j;
833 cebix 1.1 }
834    
835    
836     /*
837     * Allocate C64 colors
838     */
839    
840     void C64Display::InitColors(uint8 *colors)
841     {
842     SDL_Color palette[PALETTE_SIZE];
843     for (int i=0; i<16; i++) {
844     palette[i].r = palette_red[i];
845     palette[i].g = palette_green[i];
846     palette[i].b = palette_blue[i];
847     }
848     palette[fill_gray].r = palette[fill_gray].g = palette[fill_gray].b = 0xd0;
849     palette[shine_gray].r = palette[shine_gray].g = palette[shine_gray].b = 0xf0;
850     palette[shadow_gray].r = palette[shadow_gray].g = palette[shadow_gray].b = 0x80;
851     palette[red].r = 0xf0;
852     palette[red].g = palette[red].b = 0;
853     palette[green].g = 0xf0;
854     palette[green].r = palette[green].b = 0;
855     SDL_SetColors(screen, palette, 0, PALETTE_SIZE);
856    
857     for (int i=0; i<256; i++)
858     colors[i] = i & 0x0f;
859     }
860    
861    
862     /*
863     * Show a requester (error message)
864     */
865    
866 cebix 1.8 long int ShowRequester(const char *a, const char *b, const char *)
867 cebix 1.1 {
868     printf("%s: %s\n", a, b);
869     return 1;
870     }