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

# 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 "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
315 // Hide mouse pointer in fullscreen mode
316 if (ThePrefs.DisplayType == DISPTYPE_SCREEN)
317 SDL_ShowCursor(0);
318
319 // 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 pulse_sa.sa_flags = SA_RESTART;
327 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 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 SDL_Quit();
350
351 c64_disp = NULL;
352 }
353
354
355 /*
356 * Prefs may have changed
357 */
358
359 void C64Display::NewPrefs(Prefs *prefs)
360 {
361 // Unused, we handle fullscreen/window mode switches in PollKeyboard()
362 }
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 #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 // Update display
445 SDL_Flip(screen);
446 #endif
447 }
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 if (ThePrefs.DisplayType == DISPTYPE_WINDOW) // don't invoke in fullscreen mode
682 SAM(TheC64);
683 break;
684
685 case SDLK_F10: // F10: Prefs/Quit
686 TheC64->Pause();
687 if (ThePrefs.DisplayType == DISPTYPE_SCREEN) { // exit fullscreen mode
688 SDL_WM_ToggleFullScreen(SDL_GetVideoSurface());
689 SDL_ShowCursor(1);
690 }
691
692 if (!TheApp->RunPrefsEditor()) {
693 quit_requested = true;
694 } else {
695 if (ThePrefs.DisplayType == DISPTYPE_SCREEN) { // enter fullscreen mode
696 SDL_ShowCursor(0);
697 SDL_WM_ToggleFullScreen(SDL_GetVideoSurface());
698 }
699 }
700
701 TheC64->Resume();
702 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 #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 // 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 }
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 }
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 long int ShowRequester(const char *a, const char *b, const char *)
874 {
875 printf("%s: %s\n", a, b);
876 return 1;
877 }