ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/Display_SDL.h
Revision: 1.11
Committed: 2010-04-23T11:12:05Z (14 years ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.10: +251 -7 lines
Log Message:
added resizable display using OpenGL

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     "uniform float texSize;"
214     "void main()"
215     "{"
216     #if 0
217     // Nearest neighbour
218     " vec4 idx = texture2D(screen, gl_TexCoord[0].st);"
219     " gl_FragColor = texture1D(palette, idx.r);"
220     #else
221     // Linear interpolation
222     // (setting the GL_TEXTURE_MAG_FILTER to GL_LINEAR would interpolate
223     // the color indices which is not what we want; we need to manually
224     // interpolate the palette values instead)
225     " const float texel = 1.0 / texSize;"
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     " float s1 = fract(st.s * texSize);"
232     " float s0 = 1.0 - s1;"
233     " float t1 = fract(st.t * texSize);"
234     " float t0 = 1.0 - t1;"
235     " vec4 color00 = texture1D(palette, idx00.r) * s0 * t0;"
236     " vec4 color01 = texture1D(palette, idx01.r) * s0 * t1;"
237     " vec4 color10 = texture1D(palette, idx10.r) * s1 * t0;"
238     " vec4 color11 = texture1D(palette, idx11.r) * s1 * t1;"
239     " gl_FragColor = color00 + color01 + color10 + color11;"
240     #endif
241     "}";
242     glShaderSource(shader, 1, &src, NULL);
243     glCompileShader(shader);
244    
245     GLint status;
246     glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
247     if (status == GL_FALSE) {
248     GLint logLength = 0;
249     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
250     if (logLength > 0) {
251     GLchar *log = (GLchar *)malloc(logLength);
252     GLint actual;
253     glGetShaderInfoLog(shader, logLength, &actual, log);
254     fprintf(stderr, "%s\n", log);
255     exit(1);
256     }
257     }
258    
259     GLuint program = glCreateProgram();
260     glAttachShader(program, shader);
261     glLinkProgram(program);
262     glUseProgram(program);
263    
264     glUniform1f(glGetUniformLocation(program, "texSize"), float(TEXTURE_SIZE));
265    
266     // Create VIC display texture (8-bit color index in the red channel)
267     uint8 *tmp = (uint8 *)malloc(TEXTURE_SIZE * TEXTURE_SIZE);
268     memset(tmp, 0, TEXTURE_SIZE * TEXTURE_SIZE);
269    
270     glGenTextures(1, &vic_tex);
271     glActiveTexture(GL_TEXTURE0);
272     glBindTexture(GL_TEXTURE_2D, vic_tex);
273     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
274     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
275     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // don't interpolate color index values
276     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
277     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_SIZE, TEXTURE_SIZE, 0, GL_RED, GL_UNSIGNED_BYTE, tmp);
278     glUniform1i(glGetUniformLocation(program, "screen"), 0);
279    
280     free(tmp);
281    
282     // Create VIC palette texture
283     tmp = (uint8 *)malloc(256 * 3);
284     memset(tmp, 0xff, 256 * 3);
285     for (int i=0; i<16; ++i) {
286     tmp[i*3+0] = palette_red[i & 0x0f];
287     tmp[i*3+1] = palette_green[i & 0x0f];
288     tmp[i*3+2] = palette_blue[i & 0x0f];
289     }
290     tmp[fill_gray*3+0] = tmp[fill_gray*3+1] = tmp[fill_gray*3+2] = 0xd0;
291     tmp[shine_gray*3+0] = tmp[shine_gray*3+1] = tmp[shine_gray*3+2] = 0xf0;
292     tmp[shadow_gray*3+0] = tmp[shadow_gray*3+1] = tmp[shadow_gray*3+2] = 0x80;
293     tmp[red*3+0] = 0xf0;
294     tmp[red*3+1] = tmp[red*3+2] = 0;
295     tmp[green*3+1] = 0xf0;
296     tmp[green*3+0] = tmp[green*3+2] = 0;
297    
298     glGenTextures(1, &palette_tex);
299     glActiveTexture(GL_TEXTURE1);
300     glBindTexture(GL_TEXTURE_1D, palette_tex);
301     glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // don't interpolate palette entries
302     glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
303     glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA8, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, tmp);
304     glUniform1i(glGetUniformLocation(program, "palette"), 1);
305    
306     free(tmp);
307    
308     #else
309    
310     flags |= (SDL_HWSURFACE | SDL_DOUBLEBUF);
311     screen = SDL_SetVideoMode(FRAME_WIDTH, FRAME_HEIGHT, 8, flags);
312    
313     #endif
314 cebix 1.1
315 cebix 1.9 // Hide mouse pointer in fullscreen mode
316     if (ThePrefs.DisplayType == DISPTYPE_SCREEN)
317     SDL_ShowCursor(0);
318    
319 cebix 1.1 // LEDs off
320     for (int i=0; i<4; i++)
321     led_state[i] = old_led_state[i] = LED_OFF;
322    
323     // Start timer for LED error blinking
324     c64_disp = this;
325     pulse_sa.sa_handler = (void (*)(int))pulse_handler;
326 cebix 1.3 pulse_sa.sa_flags = SA_RESTART;
327 cebix 1.1 sigemptyset(&pulse_sa.sa_mask);
328     sigaction(SIGALRM, &pulse_sa, NULL);
329     pulse_tv.it_interval.tv_sec = 0;
330     pulse_tv.it_interval.tv_usec = 400000;
331     pulse_tv.it_value.tv_sec = 0;
332     pulse_tv.it_value.tv_usec = 400000;
333     setitimer(ITIMER_REAL, &pulse_tv, NULL);
334     }
335    
336    
337     /*
338     * Display destructor
339     */
340    
341     C64Display::~C64Display()
342     {
343 cebix 1.9 pulse_tv.it_interval.tv_sec = 0;
344     pulse_tv.it_interval.tv_usec = 0;
345     pulse_tv.it_value.tv_sec = 0;
346     pulse_tv.it_value.tv_usec = 0;
347     setitimer(ITIMER_REAL, &pulse_tv, NULL);
348    
349 cebix 1.1 SDL_Quit();
350 cebix 1.9
351     c64_disp = NULL;
352 cebix 1.1 }
353    
354    
355     /*
356     * Prefs may have changed
357     */
358    
359     void C64Display::NewPrefs(Prefs *prefs)
360     {
361 cebix 1.11 // Unused, we handle fullscreen/window mode switches in PollKeyboard()
362 cebix 1.1 }
363    
364    
365     /*
366     * Redraw bitmap
367     */
368    
369     void C64Display::Update(void)
370     {
371     // Draw speedometer/LEDs
372     SDL_Rect r = {0, DISPLAY_Y, DISPLAY_X, 15};
373     SDL_FillRect(screen, &r, fill_gray);
374     r.w = DISPLAY_X; r.h = 1;
375     SDL_FillRect(screen, &r, shine_gray);
376     r.y = DISPLAY_Y + 14;
377     SDL_FillRect(screen, &r, shadow_gray);
378     r.w = 16;
379     for (int i=2; i<6; i++) {
380     r.x = DISPLAY_X * i/5 - 24; r.y = DISPLAY_Y + 4;
381     SDL_FillRect(screen, &r, shadow_gray);
382     r.y = DISPLAY_Y + 10;
383     SDL_FillRect(screen, &r, shine_gray);
384     }
385     r.y = DISPLAY_Y; r.w = 1; r.h = 15;
386     for (int i=0; i<5; i++) {
387     r.x = DISPLAY_X * i / 5;
388     SDL_FillRect(screen, &r, shine_gray);
389     r.x = DISPLAY_X * (i+1) / 5 - 1;
390     SDL_FillRect(screen, &r, shadow_gray);
391     }
392     r.y = DISPLAY_Y + 4; r.h = 7;
393     for (int i=2; i<6; i++) {
394     r.x = DISPLAY_X * i/5 - 24;
395     SDL_FillRect(screen, &r, shadow_gray);
396     r.x = DISPLAY_X * i/5 - 9;
397     SDL_FillRect(screen, &r, shine_gray);
398     }
399     r.y = DISPLAY_Y + 5; r.w = 14; r.h = 5;
400     for (int i=0; i<4; i++) {
401     r.x = DISPLAY_X * (i+2) / 5 - 23;
402     int c;
403     switch (led_state[i]) {
404     case LED_ON:
405     c = green;
406     break;
407     case LED_ERROR_ON:
408     c = red;
409     break;
410     default:
411     c = black;
412     break;
413     }
414     SDL_FillRect(screen, &r, c);
415     }
416    
417     draw_string(screen, DISPLAY_X * 1/5 + 8, DISPLAY_Y + 4, "D\x12 8", black, fill_gray);
418     draw_string(screen, DISPLAY_X * 2/5 + 8, DISPLAY_Y + 4, "D\x12 9", black, fill_gray);
419     draw_string(screen, DISPLAY_X * 3/5 + 8, DISPLAY_Y + 4, "D\x12 10", black, fill_gray);
420     draw_string(screen, DISPLAY_X * 4/5 + 8, DISPLAY_Y + 4, "D\x12 11", black, fill_gray);
421     draw_string(screen, 24, DISPLAY_Y + 4, speedometer_string, black, fill_gray);
422    
423 cebix 1.11 #ifdef ENABLE_OPENGL
424     // Load screen to texture
425     glActiveTexture(GL_TEXTURE0);
426     glBindTexture(GL_TEXTURE_2D, vic_tex);
427     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, FRAME_WIDTH, FRAME_HEIGHT, GL_RED, GL_UNSIGNED_BYTE, screen->pixels);
428    
429     // Draw textured rectangle
430     glBegin(GL_QUADS);
431     glTexCoord2f(0.0, 0.0);
432     glVertex2f(0.0, 0.0);
433     glTexCoord2f(float(FRAME_WIDTH) / TEXTURE_SIZE, 0.0);
434     glVertex2f(float(FRAME_WIDTH), 0.0);
435     glTexCoord2f(float(FRAME_WIDTH) / TEXTURE_SIZE, float(FRAME_HEIGHT) / TEXTURE_SIZE);
436     glVertex2f(float(FRAME_WIDTH), float(FRAME_HEIGHT));
437     glTexCoord2f(0.0, float(FRAME_HEIGHT) / TEXTURE_SIZE);
438     glVertex2f(0.0, float(FRAME_HEIGHT));
439     glEnd();
440    
441     // Update display
442     SDL_GL_SwapBuffers();
443     #else
444 cebix 1.1 // Update display
445     SDL_Flip(screen);
446 cebix 1.11 #endif
447 cebix 1.1 }
448    
449    
450     /*
451     * Draw string into surface using the C64 ROM font
452     */
453    
454     void C64Display::draw_string(SDL_Surface *s, int x, int y, const char *str, uint8 front_color, uint8 back_color)
455     {
456     uint8 *pb = (uint8 *)s->pixels + s->pitch*y + x;
457     char c;
458     while ((c = *str++) != 0) {
459     uint8 *q = TheC64->Char + c*8 + 0x800;
460     uint8 *p = pb;
461     for (int y=0; y<8; y++) {
462     uint8 v = *q++;
463     p[0] = (v & 0x80) ? front_color : back_color;
464     p[1] = (v & 0x40) ? front_color : back_color;
465     p[2] = (v & 0x20) ? front_color : back_color;
466     p[3] = (v & 0x10) ? front_color : back_color;
467     p[4] = (v & 0x08) ? front_color : back_color;
468     p[5] = (v & 0x04) ? front_color : back_color;
469     p[6] = (v & 0x02) ? front_color : back_color;
470     p[7] = (v & 0x01) ? front_color : back_color;
471     p += s->pitch;
472     }
473     pb += 8;
474     }
475     }
476    
477    
478     /*
479     * LED error blink
480     */
481    
482     void C64Display::pulse_handler(...)
483     {
484     for (int i=0; i<4; i++)
485     switch (c64_disp->led_state[i]) {
486     case LED_ERROR_ON:
487     c64_disp->led_state[i] = LED_ERROR_OFF;
488     break;
489     case LED_ERROR_OFF:
490     c64_disp->led_state[i] = LED_ERROR_ON;
491     break;
492     }
493     }
494    
495    
496     /*
497     * Draw speedometer
498     */
499    
500     void C64Display::Speedometer(int speed)
501     {
502     static int delay = 0;
503    
504     if (delay >= 20) {
505     delay = 0;
506     sprintf(speedometer_string, "%d%%", speed);
507     } else
508     delay++;
509     }
510    
511    
512     /*
513     * Return pointer to bitmap data
514     */
515    
516     uint8 *C64Display::BitmapBase(void)
517     {
518     return (uint8 *)screen->pixels;
519     }
520    
521    
522     /*
523     * Return number of bytes per row
524     */
525    
526     int C64Display::BitmapXMod(void)
527     {
528     return screen->pitch;
529     }
530    
531    
532     /*
533     * Poll the keyboard
534     */
535    
536     static void translate_key(SDLKey key, bool key_up, uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick)
537     {
538     int c64_key = -1;
539     switch (key) {
540     case SDLK_a: c64_key = MATRIX(1,2); break;
541     case SDLK_b: c64_key = MATRIX(3,4); break;
542     case SDLK_c: c64_key = MATRIX(2,4); break;
543     case SDLK_d: c64_key = MATRIX(2,2); break;
544     case SDLK_e: c64_key = MATRIX(1,6); break;
545     case SDLK_f: c64_key = MATRIX(2,5); break;
546     case SDLK_g: c64_key = MATRIX(3,2); break;
547     case SDLK_h: c64_key = MATRIX(3,5); break;
548     case SDLK_i: c64_key = MATRIX(4,1); break;
549     case SDLK_j: c64_key = MATRIX(4,2); break;
550     case SDLK_k: c64_key = MATRIX(4,5); break;
551     case SDLK_l: c64_key = MATRIX(5,2); break;
552     case SDLK_m: c64_key = MATRIX(4,4); break;
553     case SDLK_n: c64_key = MATRIX(4,7); break;
554     case SDLK_o: c64_key = MATRIX(4,6); break;
555     case SDLK_p: c64_key = MATRIX(5,1); break;
556     case SDLK_q: c64_key = MATRIX(7,6); break;
557     case SDLK_r: c64_key = MATRIX(2,1); break;
558     case SDLK_s: c64_key = MATRIX(1,5); break;
559     case SDLK_t: c64_key = MATRIX(2,6); break;
560     case SDLK_u: c64_key = MATRIX(3,6); break;
561     case SDLK_v: c64_key = MATRIX(3,7); break;
562     case SDLK_w: c64_key = MATRIX(1,1); break;
563     case SDLK_x: c64_key = MATRIX(2,7); break;
564     case SDLK_y: c64_key = MATRIX(3,1); break;
565     case SDLK_z: c64_key = MATRIX(1,4); break;
566    
567     case SDLK_0: c64_key = MATRIX(4,3); break;
568     case SDLK_1: c64_key = MATRIX(7,0); break;
569     case SDLK_2: c64_key = MATRIX(7,3); break;
570     case SDLK_3: c64_key = MATRIX(1,0); break;
571     case SDLK_4: c64_key = MATRIX(1,3); break;
572     case SDLK_5: c64_key = MATRIX(2,0); break;
573     case SDLK_6: c64_key = MATRIX(2,3); break;
574     case SDLK_7: c64_key = MATRIX(3,0); break;
575     case SDLK_8: c64_key = MATRIX(3,3); break;
576     case SDLK_9: c64_key = MATRIX(4,0); break;
577    
578     case SDLK_SPACE: c64_key = MATRIX(7,4); break;
579     case SDLK_BACKQUOTE: c64_key = MATRIX(7,1); break;
580     case SDLK_BACKSLASH: c64_key = MATRIX(6,6); break;
581     case SDLK_COMMA: c64_key = MATRIX(5,7); break;
582     case SDLK_PERIOD: c64_key = MATRIX(5,4); break;
583     case SDLK_MINUS: c64_key = MATRIX(5,0); break;
584     case SDLK_EQUALS: c64_key = MATRIX(5,3); break;
585     case SDLK_LEFTBRACKET: c64_key = MATRIX(5,6); break;
586     case SDLK_RIGHTBRACKET: c64_key = MATRIX(6,1); break;
587     case SDLK_SEMICOLON: c64_key = MATRIX(5,5); break;
588     case SDLK_QUOTE: c64_key = MATRIX(6,2); break;
589     case SDLK_SLASH: c64_key = MATRIX(6,7); break;
590    
591     case SDLK_ESCAPE: c64_key = MATRIX(7,7); break;
592     case SDLK_RETURN: c64_key = MATRIX(0,1); break;
593     case SDLK_BACKSPACE: case SDLK_DELETE: c64_key = MATRIX(0,0); break;
594     case SDLK_INSERT: c64_key = MATRIX(6,3); break;
595     case SDLK_HOME: c64_key = MATRIX(6,3); break;
596     case SDLK_END: c64_key = MATRIX(6,0); break;
597     case SDLK_PAGEUP: c64_key = MATRIX(6,0); break;
598     case SDLK_PAGEDOWN: c64_key = MATRIX(6,5); break;
599    
600     case SDLK_LCTRL: case SDLK_TAB: c64_key = MATRIX(7,2); break;
601     case SDLK_RCTRL: c64_key = MATRIX(7,5); break;
602     case SDLK_LSHIFT: c64_key = MATRIX(1,7); break;
603     case SDLK_RSHIFT: c64_key = MATRIX(6,4); break;
604     case SDLK_LALT: case SDLK_LMETA: c64_key = MATRIX(7,5); break;
605     case SDLK_RALT: case SDLK_RMETA: c64_key = MATRIX(7,5); break;
606    
607     case SDLK_UP: c64_key = MATRIX(0,7)| 0x80; break;
608     case SDLK_DOWN: c64_key = MATRIX(0,7); break;
609     case SDLK_LEFT: c64_key = MATRIX(0,2) | 0x80; break;
610     case SDLK_RIGHT: c64_key = MATRIX(0,2); break;
611    
612     case SDLK_F1: c64_key = MATRIX(0,4); break;
613     case SDLK_F2: c64_key = MATRIX(0,4) | 0x80; break;
614     case SDLK_F3: c64_key = MATRIX(0,5); break;
615     case SDLK_F4: c64_key = MATRIX(0,5) | 0x80; break;
616     case SDLK_F5: c64_key = MATRIX(0,6); break;
617     case SDLK_F6: c64_key = MATRIX(0,6) | 0x80; break;
618     case SDLK_F7: c64_key = MATRIX(0,3); break;
619     case SDLK_F8: c64_key = MATRIX(0,3) | 0x80; break;
620    
621     case SDLK_KP0: case SDLK_KP5: c64_key = 0x10 | 0x40; break;
622     case SDLK_KP1: c64_key = 0x06 | 0x40; break;
623     case SDLK_KP2: c64_key = 0x02 | 0x40; break;
624     case SDLK_KP3: c64_key = 0x0a | 0x40; break;
625     case SDLK_KP4: c64_key = 0x04 | 0x40; break;
626     case SDLK_KP6: c64_key = 0x08 | 0x40; break;
627     case SDLK_KP7: c64_key = 0x05 | 0x40; break;
628     case SDLK_KP8: c64_key = 0x01 | 0x40; break;
629     case SDLK_KP9: c64_key = 0x09 | 0x40; break;
630    
631     case SDLK_KP_DIVIDE: c64_key = MATRIX(6,7); break;
632     case SDLK_KP_ENTER: c64_key = MATRIX(0,1); break;
633     default: break;
634     }
635    
636     if (c64_key < 0)
637     return;
638    
639     // Handle joystick emulation
640     if (c64_key & 0x40) {
641     c64_key &= 0x1f;
642     if (key_up)
643     *joystick |= c64_key;
644     else
645     *joystick &= ~c64_key;
646     return;
647     }
648    
649     // Handle other keys
650     bool shifted = c64_key & 0x80;
651     int c64_byte = (c64_key >> 3) & 7;
652     int c64_bit = c64_key & 7;
653     if (key_up) {
654     if (shifted) {
655     key_matrix[6] |= 0x10;
656     rev_matrix[4] |= 0x40;
657     }
658     key_matrix[c64_byte] |= (1 << c64_bit);
659     rev_matrix[c64_bit] |= (1 << c64_byte);
660     } else {
661     if (shifted) {
662     key_matrix[6] &= 0xef;
663     rev_matrix[4] &= 0xbf;
664     }
665     key_matrix[c64_byte] &= ~(1 << c64_bit);
666     rev_matrix[c64_bit] &= ~(1 << c64_byte);
667     }
668     }
669    
670     void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick)
671     {
672     SDL_Event event;
673     while (SDL_PollEvent(&event)) {
674     switch (event.type) {
675    
676     // Key pressed
677     case SDL_KEYDOWN:
678     switch (event.key.keysym.sym) {
679    
680     case SDLK_F9: // F9: Invoke SAM
681 cebix 1.9 if (ThePrefs.DisplayType == DISPTYPE_WINDOW) // don't invoke in fullscreen mode
682     SAM(TheC64);
683 cebix 1.1 break;
684    
685 cebix 1.9 case SDLK_F10: // F10: Prefs/Quit
686     TheC64->Pause();
687     if (ThePrefs.DisplayType == DISPTYPE_SCREEN) { // exit fullscreen mode
688 cebix 1.11 SDL_WM_ToggleFullScreen(SDL_GetVideoSurface());
689 cebix 1.9 SDL_ShowCursor(1);
690     }
691    
692     if (!TheApp->RunPrefsEditor()) {
693     quit_requested = true;
694 cebix 1.11 } else {
695     if (ThePrefs.DisplayType == DISPTYPE_SCREEN) { // enter fullscreen mode
696     SDL_ShowCursor(0);
697     SDL_WM_ToggleFullScreen(SDL_GetVideoSurface());
698     }
699 cebix 1.9 }
700    
701     TheC64->Resume();
702 cebix 1.1 break;
703    
704     case SDLK_F11: // F11: NMI (Restore)
705     TheC64->NMI();
706     break;
707    
708     case SDLK_F12: // F12: Reset
709     TheC64->Reset();
710     break;
711    
712     case SDLK_NUMLOCK:
713     num_locked = true;
714     break;
715    
716     case SDLK_KP_PLUS: // '+' on keypad: Increase SkipFrames
717     ThePrefs.SkipFrames++;
718     break;
719    
720     case SDLK_KP_MINUS: // '-' on keypad: Decrease SkipFrames
721     if (ThePrefs.SkipFrames > 1)
722     ThePrefs.SkipFrames--;
723     break;
724    
725     case SDLK_KP_MULTIPLY: // '*' on keypad: Toggle speed limiter
726     ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed;
727     break;
728    
729     default:
730     translate_key(event.key.keysym.sym, false, key_matrix, rev_matrix, joystick);
731     break;
732     }
733     break;
734    
735     // Key released
736     case SDL_KEYUP:
737     if (event.key.keysym.sym == SDLK_NUMLOCK)
738     num_locked = false;
739     else
740     translate_key(event.key.keysym.sym, true, key_matrix, rev_matrix, joystick);
741     break;
742    
743 cebix 1.11 #ifdef ENABLE_OPENGL
744     // Window resized
745     case SDL_VIDEORESIZE:
746     video_resized(event.resize.w, event.resize.h);
747     break;
748     #endif
749    
750 cebix 1.1 // Quit Frodo
751     case SDL_QUIT:
752     quit_requested = true;
753     break;
754     }
755     }
756     }
757    
758    
759     /*
760     * Check if NumLock is down (for switching the joystick keyboard emulation)
761     */
762    
763     bool C64Display::NumLock(void)
764     {
765     return num_locked;
766 cebix 1.4 }
767    
768    
769     /*
770     * Open/close joystick drivers given old and new state of
771     * joystick preferences
772     */
773    
774     void C64::open_close_joystick(int port, int oldjoy, int newjoy)
775     {
776     if (oldjoy != newjoy) {
777     joy_minx[port] = joy_miny[port] = 32767; // Reset calibration
778     joy_maxx[port] = joy_maxy[port] = -32768;
779     if (newjoy) {
780     joy[port] = SDL_JoystickOpen(newjoy - 1);
781     if (joy[port] == NULL)
782     fprintf(stderr, "Couldn't open joystick %d\n", port + 1);
783     } else {
784     if (joy[port]) {
785     SDL_JoystickClose(joy[port]);
786     joy[port] = NULL;
787     }
788     }
789     }
790     }
791    
792     void C64::open_close_joysticks(int oldjoy1, int oldjoy2, int newjoy1, int newjoy2)
793     {
794     open_close_joystick(0, oldjoy1, newjoy1);
795     open_close_joystick(1, oldjoy2, newjoy2);
796     }
797    
798    
799     /*
800     * Poll joystick port, return CIA mask
801     */
802    
803     uint8 C64::poll_joystick(int port)
804     {
805     uint8 j = 0xff;
806    
807     if (port == 0 && (joy[0] || joy[1]))
808     SDL_JoystickUpdate();
809    
810     if (joy[port]) {
811     int x = SDL_JoystickGetAxis(joy[port], 0), y = SDL_JoystickGetAxis(joy[port], 1);
812    
813     if (x > joy_maxx[port])
814     joy_maxx[port] = x;
815     if (x < joy_minx[port])
816     joy_minx[port] = x;
817     if (y > joy_maxy[port])
818     joy_maxy[port] = y;
819     if (y < joy_miny[port])
820     joy_miny[port] = y;
821    
822     if (joy_maxx[port] - joy_minx[port] < 100 || joy_maxy[port] - joy_miny[port] < 100)
823     return 0xff;
824    
825     if (x < (joy_minx[port] + (joy_maxx[port]-joy_minx[port])/3))
826     j &= 0xfb; // Left
827     else if (x > (joy_minx[port] + 2*(joy_maxx[port]-joy_minx[port])/3))
828     j &= 0xf7; // Right
829    
830     if (y < (joy_miny[port] + (joy_maxy[port]-joy_miny[port])/3))
831     j &= 0xfe; // Up
832     else if (y > (joy_miny[port] + 2*(joy_maxy[port]-joy_miny[port])/3))
833     j &= 0xfd; // Down
834    
835     if (SDL_JoystickGetButton(joy[port], 0))
836     j &= 0xef; // Button
837     }
838    
839     return j;
840 cebix 1.1 }
841    
842    
843     /*
844     * Allocate C64 colors
845     */
846    
847     void C64Display::InitColors(uint8 *colors)
848     {
849     SDL_Color palette[PALETTE_SIZE];
850     for (int i=0; i<16; i++) {
851     palette[i].r = palette_red[i];
852     palette[i].g = palette_green[i];
853     palette[i].b = palette_blue[i];
854     }
855     palette[fill_gray].r = palette[fill_gray].g = palette[fill_gray].b = 0xd0;
856     palette[shine_gray].r = palette[shine_gray].g = palette[shine_gray].b = 0xf0;
857     palette[shadow_gray].r = palette[shadow_gray].g = palette[shadow_gray].b = 0x80;
858     palette[red].r = 0xf0;
859     palette[red].g = palette[red].b = 0;
860     palette[green].g = 0xf0;
861     palette[green].r = palette[green].b = 0;
862     SDL_SetColors(screen, palette, 0, PALETTE_SIZE);
863    
864     for (int i=0; i<256; i++)
865     colors[i] = i & 0x0f;
866     }
867    
868    
869     /*
870     * Show a requester (error message)
871     */
872    
873 cebix 1.8 long int ShowRequester(const char *a, const char *b, const char *)
874 cebix 1.1 {
875     printf("%s: %s\n", a, b);
876     return 1;
877     }