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 (12 years, 7 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

# Content
1 /*
2 * Display_SDL.h - C64 graphics display, emulator window handling,
3 * SDL specific stuff
4 *
5 * Frodo Copyright (C) Christian Bauer
6 *
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 #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
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 // SDL joysticks
49 static SDL_Joystick *joy[2] = {NULL, NULL};
50
51 // 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 #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 /*
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 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO) < 0) {
101 fprintf(stderr, "Couldn't initialize SDL (%s)\n", SDL_GetError());
102 return 0;
103 }
104
105 return 1;
106 }
107
108
109 #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 /*
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
168 // Open window
169 SDL_WM_SetCaption(VERSION_STRING, "Frodo");
170 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 "const float texSize = 512.0;" // texture size
214 "const float texel = 1.0 / texSize;"
215 "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 " 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 #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
308 // Hide mouse pointer in fullscreen mode
309 if (ThePrefs.DisplayType == DISPTYPE_SCREEN)
310 SDL_ShowCursor(0);
311
312 // 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 pulse_sa.sa_flags = SA_RESTART;
320 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 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 SDL_Quit();
343
344 c64_disp = NULL;
345 }
346
347
348 /*
349 * Prefs may have changed
350 */
351
352 void C64Display::NewPrefs(Prefs *prefs)
353 {
354 // Unused, we handle fullscreen/window mode switches in PollKeyboard()
355 }
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 #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 // Update display
438 SDL_Flip(screen);
439 #endif
440 }
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 if (ThePrefs.DisplayType == DISPTYPE_WINDOW) // don't invoke in fullscreen mode
675 SAM(TheC64);
676 break;
677
678 case SDLK_F10: // F10: Prefs/Quit
679 TheC64->Pause();
680 if (ThePrefs.DisplayType == DISPTYPE_SCREEN) { // exit fullscreen mode
681 SDL_WM_ToggleFullScreen(SDL_GetVideoSurface());
682 SDL_ShowCursor(1);
683 }
684
685 if (!TheApp->RunPrefsEditor()) {
686 quit_requested = true;
687 } else {
688 if (ThePrefs.DisplayType == DISPTYPE_SCREEN) { // enter fullscreen mode
689 SDL_ShowCursor(0);
690 SDL_WM_ToggleFullScreen(SDL_GetVideoSurface());
691 }
692 }
693
694 TheC64->Resume();
695 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 #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 // 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 }
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 }
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 long int ShowRequester(const char *a, const char *b, const char *)
867 {
868 printf("%s: %s\n", a, b);
869 return 1;
870 }