debugger is using new text screen intefrace now too
[zymosis.git] / src / ZXEmuT / libvideo / video.c
blob249e498c58b56c8afabc87e89ff23511952d6756
1 /***************************************************************************
3 * ZXEmuT -- ZX Spectrum Emulator with Tcl scripting
5 * Copyright (C) 2012-2020 Ketmar Dark <ketmar@ketmar.no-ip.org>
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, version 3 of the License ONLY.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **************************************************************************/
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <time.h>
25 #include "video.h"
27 #include "zxpal.c"
28 #include "vdfont6x8.c"
29 #include "msxfont8x8.c"
30 #include "fonts_8_16.c"
32 #include "zfnt.c"
35 ////////////////////////////////////////////////////////////////////////////////
36 FrameCB frameCB = NULL;
37 KeyCB keyCB = NULL;
38 MouseCB mouseCB = NULL;
39 MouseButtonCB mouseButtonCB = NULL;
40 WindowActivationCB windowActivationCB = NULL;
41 int vidScale = 3; // 1, 2, 3, 4; default is 3
43 void (*vidBeforeFlipCB) (SDL_Surface *sfc) = NULL;
45 VidTChar vid_textscr[VID_TEXT_WIDTH*VID_TEXT_HEIGHT];
46 int vid_textscr_active = 0;
47 int vid_textsrc_height = 0;
50 // ////////////////////////////////////////////////////////////////////////// //
51 static int actualScale;
54 // ////////////////////////////////////////////////////////////////////////// //
55 static void screenFlip (void);
58 ////////////////////////////////////////////////////////////////////////////////
59 int optFullscreen = 0;
60 int optTVScaler = 1;
61 SDL_Surface *screen = NULL;
62 SDL_Surface *screenReal = NULL;
63 Uint32 palette[256]; // converted
64 unsigned paletteUsed = 0;
65 static unsigned paletteUsedCurr = 0;
66 SDL_Surface *frameSfc = NULL;
67 int vidWindowActivated = 1;
68 int vidColorMode = 0; // 0: ok; 1: b/w; 2: green
69 static SurfaceLock frameSfcLock;
70 static int frameSfcRaped = 0;
72 static uint8_t pal8bit[256][4]; // [3] is unused
73 uint8_t palRGB[256][4]; // [3] is unused
74 int vidRIdx, vidGIdx, vidBIdx;
77 ////////////////////////////////////////////////////////////////////////////////
78 static void initEventSystem (void);
81 ////////////////////////////////////////////////////////////////////////////////
82 static void quitCleanupCB (void) {
83 SDL_Quit();
87 // ////////////////////////////////////////////////////////////////////////// //
88 void vt_cls (uint8_t ch, uint8_t attr) {
89 VidTChar *d = vid_textscr;
90 uint32_t left = VID_TEXT_WIDTH*VID_TEXT_HEIGHT;
91 while (left--) {
92 d->ch = ch;
93 d->attr = attr;
94 ++d;
99 void vt_writechar (int x, int y, uint8_t ch, uint8_t attr) {
100 if (x < 0 || y < 0 || x >= VID_TEXT_WIDTH || y >= VID_TEXT_HEIGHT) return;
101 VidTChar *d = vid_textscr+y*VID_TEXT_WIDTH+x;
102 d->ch = ch;
103 d->attr = attr;
107 void vt_writechars (int x, int y, int count, uint8_t ch, uint8_t attr) {
108 if (count <= 0 || y < 0 || x >= VID_TEXT_WIDTH || y >= VID_TEXT_HEIGHT) return;
109 if (x < 0) {
110 count += x;
111 if (count <= 0) return;
112 x = 0;
114 if (VID_TEXT_WIDTH-x < count) count = VID_TEXT_WIDTH-x;
115 VidTChar *d = vid_textscr+y*VID_TEXT_WIDTH+x;
116 while (count--) {
117 d->ch = ch;
118 d->attr = attr;
119 ++d;
124 void vt_writestrcnt (int x, int y, const char *s, size_t slen, uint8_t attr) {
125 if (!slen || !s) return;
126 if (y < 0 || x >= VID_TEXT_WIDTH || y >= VID_TEXT_HEIGHT) return;
127 if (slen > 0x3fffffffU) slen = 0x3fffffffU;
128 int count = (int)slen;
129 if (x < 0) {
130 count += x;
131 if (count <= 0) return;
132 x = 0;
134 if (VID_TEXT_WIDTH-x < count) count = VID_TEXT_WIDTH-x;
135 VidTChar *d = vid_textscr+y*VID_TEXT_WIDTH+x;
136 while (count--) {
137 d->ch = *(const uint8_t *)s;
138 d->attr = attr;
139 ++d;
140 ++s;
145 void vt_writestrz (int x, int y, const char *s, uint8_t attr) {
146 if (!s || !s[0]) return;
147 if (y < 0 || x >= VID_TEXT_WIDTH || y >= VID_TEXT_HEIGHT) return;
148 vt_writestrcnt(x, y, s, strlen(s), attr);
152 ////////////////////////////////////////////////////////////////////////////////
153 static struct timespec timerStart;
154 static int timerInitialized = 0;
155 static int timerSource = -1;
156 #define TIMER_CLOCK_TYPE CLOCK_MONOTONIC_RAW
157 //#define TIMER_CLOCK_TYPE CLOCK_MONOTONIC
160 static int timerCheckClockSource (void) {
161 FILE *fl = fopen("/sys/devices/system/clocksource/clocksource0/current_clocksource", "r");
162 char buf[128];
164 if (fl == NULL) {
165 fprintf(stderr, "WARNING: can't determine clock source!\n");
166 return TIMER_OTHER;
168 memset(buf, 0, sizeof(buf));
169 if (fgets(buf, sizeof(buf)-1, fl) == NULL) {
170 fprintf(stderr, "WARNING: can't determine clock source!\n");
171 fclose(fl);
172 return TIMER_OTHER;
174 fclose(fl);
175 while (buf[0] && isspace(buf[strlen(buf)-1])) buf[strlen(buf)-1] = 0;
176 //fprintf(stderr, "clock source: %s\n", buf);
177 return (strcasecmp(buf, "hpet") == 0 ? TIMER_HPET : TIMER_OTHER);
181 int timerInit (void) {
182 if (!timerInitialized) {
183 struct timespec cres;
185 timerInitialized = 1;
187 if ((timerSource = timerCheckClockSource()) < 0) return timerSource;
189 if (clock_getres(TIMER_CLOCK_TYPE, &cres) != 0) {
190 fprintf(stderr, "ERROR: can't get clock resolution!\n");
191 return TIMER_ERROR;
193 //fprintf(stderr, "CLOCK_MONOTONIC: %ld:%ld\n", cres.tv_sec, cres.tv_nsec);
195 if (cres.tv_sec > 0 || cres.tv_nsec > (long)1000000*10 /*10 ms*/) {
196 fprintf(stderr, "ERROR: real-time clock resolution is too low!\n");
197 return TIMER_ERROR;
200 if (clock_gettime(TIMER_CLOCK_TYPE, &timerStart) != 0) {
201 fprintf(stderr, "ERROR: can't get real-time clock value!\n");
202 return TIMER_ERROR;
206 return timerSource;
210 int timerReinit (void) {
211 if (clock_gettime(TIMER_CLOCK_TYPE, &timerStart) != 0) {
212 fprintf(stderr, "ERROR: can't get real-time clock value!\n");
213 return -1;
216 return 0;
220 int64_t timerGetMS (void) {
221 struct timespec ts;
223 if (!timerInitialized) {
224 if (timerInit() < 0) return -1;
225 timerInitialized = 1;
228 if (clock_gettime(TIMER_CLOCK_TYPE, &ts) != 0) {
229 fprintf(stderr, "ERROR: can't get real-time clock value!\n");
230 return -1;
232 // ah, ignore nanoseconds in timerStart here: we need only 'differential' time, and it can start with something weird
233 return ((int64_t)(ts.tv_sec-timerStart.tv_sec))*1000+(ts.tv_nsec-timerStart.tv_nsec)/1000000;
237 int64_t timerGetMicroSeconds (void) {
238 struct timespec ts;
240 if (!timerInitialized) {
241 if (timerInit() < 0) return -1;
242 timerInitialized = 1;
245 if (clock_gettime(TIMER_CLOCK_TYPE, &ts) != 0) {
246 fprintf(stderr, "ERROR: can't get real-time clock value!\n");
247 return -1;
249 // ah, ignore nanoseconds in timerStart here: we need only 'differential' time, and it can start with something weird
250 return ((int64_t)(ts.tv_sec-timerStart.tv_sec))*1000000+(ts.tv_nsec-timerStart.tv_nsec)/1000;
254 ////////////////////////////////////////////////////////////////////////////////
255 void sdlInit (void) {
256 if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER /*| SDL_INIT_AUDIO*/) < 0) {
257 fprintf(stderr, "FATAL: can't set videomode!\n");
258 exit(1);
260 SDL_WM_SetCaption("ZXEmuT", "ZXEmuT");
261 SDL_EnableKeyRepeat(200, 25);
262 SDL_EnableUNICODE(1);
263 //SDL_EnableUNICODE(0);
264 atexit(quitCleanupCB);
268 #define COLORCUBE_BITS (6)
269 #define COLORCUBE_LINE_SIZE (1u<<COLORCUBE_BITS)
270 static uint8_t colorcube[COLORCUBE_LINE_SIZE*COLORCUBE_LINE_SIZE*COLORCUBE_LINE_SIZE];
271 static uint8_t colorcube2[COLORCUBE_LINE_SIZE*COLORCUBE_LINE_SIZE*COLORCUBE_LINE_SIZE];
272 static int cc2built = 0;
274 static inline void ccubeSet (uint8_t palidx, uint8_t r, uint8_t g, uint8_t b) {
275 #if COLORCUBE_BITS < 8
276 r >>= 8-COLORCUBE_BITS;
277 g >>= 8-COLORCUBE_BITS;
278 b >>= 8-COLORCUBE_BITS;
279 #endif
280 colorcube[r*(COLORCUBE_LINE_SIZE*COLORCUBE_LINE_SIZE)+g*COLORCUBE_LINE_SIZE+b] = palidx;
284 static inline uint8_t ccubeGet (uint8_t r, uint8_t g, uint8_t b) {
285 #if COLORCUBE_BITS < 8
286 r >>= 8-COLORCUBE_BITS;
287 g >>= 8-COLORCUBE_BITS;
288 b >>= 8-COLORCUBE_BITS;
289 #endif
290 return colorcube[r*(COLORCUBE_LINE_SIZE*COLORCUBE_LINE_SIZE)+g*COLORCUBE_LINE_SIZE+b];
294 static inline uint8_t ccube2Get (uint8_t r, uint8_t g, uint8_t b) {
295 #if COLORCUBE_BITS < 8
296 r >>= 8-COLORCUBE_BITS;
297 g >>= 8-COLORCUBE_BITS;
298 b >>= 8-COLORCUBE_BITS;
299 #endif
300 return colorcube2[r*(COLORCUBE_LINE_SIZE*COLORCUBE_LINE_SIZE)+g*COLORCUBE_LINE_SIZE+b];
304 static inline int32_t rgbDistanceSquared (uint8_t r0, uint8_t g0, uint8_t b0, uint8_t r1, uint8_t g1, uint8_t b1) {
305 const int32_t rmean = ((int32_t)r0+(int32_t)r1)/2;
306 const int32_t r = (int32_t)r0-(int32_t)r1;
307 const int32_t g = (int32_t)g0-(int32_t)g1;
308 const int32_t b = (int32_t)b0-(int32_t)b1;
309 return (((512+rmean)*r*r)/256)+4*g*g+(((767-rmean)*b*b)/256);
313 static void buildCC2 (void) {
314 if (cc2built) return;
315 for (int ir = 0; ir < COLORCUBE_LINE_SIZE; ++ir) {
316 for (int ig = 0; ig < COLORCUBE_LINE_SIZE; ++ig) {
317 for (int ib = 0; ib < COLORCUBE_LINE_SIZE; ++ib) {
318 const int r = (int)(ir*255.0f/((float)(COLORCUBE_LINE_SIZE-1))/*+0.5f*/);
319 const int g = (int)(ig*255.0f/((float)(COLORCUBE_LINE_SIZE-1))/*+0.5f*/);
320 const int b = (int)(ib*255.0f/((float)(COLORCUBE_LINE_SIZE-1))/*+0.5f*/);
321 int best_color = -1;
322 int best_dist = 0x7fffffff;
323 for (int i = 0; i < 255; ++i) {
324 int32_t dist = rgbDistanceSquared(pal8bit[i][0], pal8bit[i][1], pal8bit[i][2], r, g, b);
325 if (best_color < 0 || dist < best_dist) {
326 best_color = i;
327 best_dist = dist;
328 if (!dist) break;
331 colorcube2[ir*(COLORCUBE_LINE_SIZE*COLORCUBE_LINE_SIZE)+ig*COLORCUBE_LINE_SIZE+ib] = best_color;
335 cc2built = 1;
339 void vidResetPalette (void) {
340 paletteUsedCurr = paletteUsed;
344 uint8_t vidAllocColor (int r, int g, int b) {
345 const uint8_t r8 = (r < 0 ? 0 : r > 255 ? 255 : (uint8_t)r);
346 const uint8_t g8 = (g < 0 ? 0 : g > 255 ? 255 : (uint8_t)g);
347 const uint8_t b8 = (b < 0 ? 0 : b > 255 ? 255 : (uint8_t)b);
348 uint8_t v = ccubeGet(r8, g8, b8);
349 if (v != 255) return v;
350 if (paletteUsedCurr < 255) {
351 // alloc
352 palette[paletteUsedCurr] = SDL_MapRGB(screen->format, r8, g8, b8);
353 SDL_GetRGB(palette[paletteUsedCurr], screen->format, &palRGB[paletteUsedCurr][0], &palRGB[paletteUsedCurr][1], &palRGB[paletteUsedCurr][2]);
354 pal8bit[paletteUsedCurr][0] = r8;
355 pal8bit[paletteUsedCurr][1] = g8;
356 pal8bit[paletteUsedCurr][2] = b8;
357 ccubeSet(paletteUsedCurr, r8, g8, b8);
358 return paletteUsedCurr++;
360 // can't alloc, find nearest
361 buildCC2();
362 return ccube2Get(r8, g8, b8);
366 static void buildColorCache (void) {
367 unsigned f;
368 memset(colorcube, 255, sizeof(colorcube));
369 memset(pal8bit, 0, sizeof(pal8bit));
370 cc2built = 0;
371 for (f = 0; f < sizeof(zxPalette)/3; ++f) {
372 palette[f] = SDL_MapRGB(screen->format, zxPalette[f*3+0], zxPalette[f*3+1], zxPalette[f*3+2]);
373 ccubeSet(f, zxPalette[f*3+0], zxPalette[f*3+1], zxPalette[f*3+2]);
374 pal8bit[f][0] = zxPalette[f*3+0];
375 pal8bit[f][1] = zxPalette[f*3+1];
376 pal8bit[f][2] = zxPalette[f*3+2];
378 paletteUsed = paletteUsedCurr = f;
379 for (; f < 256; ++f) palette[f] = SDL_MapRGB(screen->format, 0, 0, 0);
380 for (f = 0; f < 256; ++f) {
381 SDL_GetRGB(palette[f], screen->format, &palRGB[f][0], &palRGB[f][1], &palRGB[f][2]);
382 palRGB[f][3] = 0;
387 void rebuildPalette (void) {
388 if (screen) buildColorCache();
392 static void lvCreateSurfaces (void) {
393 if (optFullscreen) {
394 screenReal = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE | SDL_FULLSCREEN);
395 actualScale = 2;
396 screen = SDL_CreateRGBSurface(SDL_SWSURFACE, 320, 240, screenReal->format->BitsPerPixel, screenReal->format->Rmask, screenReal->format->Gmask, screenReal->format->Bmask, 0);
397 } else {
398 actualScale = vidScale;
399 if (actualScale < 1) actualScale = 1; else if (actualScale > 4) actualScale = 4;
400 screenReal = SDL_SetVideoMode(320*actualScale, 240*actualScale, 32, SDL_SWSURFACE/*|SDL_DOUBLEBUF*/);
401 screen = SDL_CreateRGBSurface(SDL_SWSURFACE, 320, 240, screenReal->format->BitsPerPixel, screenReal->format->Rmask, screenReal->format->Gmask, screenReal->format->Bmask, 0);
403 if (screen == NULL) {
404 fprintf(stderr, "FATAL: can't set videomode!\n");
405 exit(1);
408 if (screen->format->BitsPerPixel != 32 || (screenReal != NULL && screenReal->format->BitsPerPixel != 32) ||
409 (screen->format->Rshift%8 || screen->format->Gshift%8 || screen->format->Bshift%8)) {
410 fprintf(stderr, "FATAL: videomode initialization error!\n");
411 //fprintf(stderr, "Rmask=0x%08x; Gmask=0x%08x; Bmask=0x%08x\n", screen->format->Rmask, screen->format->Gmask, screen->format->Bmask);
412 SDL_Quit();
413 exit(1);
416 vidRIdx = screen->format->Rshift/8;
417 vidGIdx = screen->format->Gshift/8;
418 vidBIdx = screen->format->Bshift/8;
419 //fprintf(stderr, "BPP: %u\n", screen->format->BytesPerPixel);
420 //SDL_ShowCursor(0);
421 buildColorCache();
425 void initVideo (void) {
426 lvCreateSurfaces();
427 initEventSystem();
431 void switchFullScreen (void) {
432 const int needInit = !!screen;
433 #if 0
434 if (optFullscreen) {
435 screenReal = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE/*|SDL_DOUBLEBUF*/);
436 } else {
437 screenReal = SDL_SetVideoMode(320*3, 240*3, 32, SDL_SWSURFACE|SDL_FULLSCREEN);
439 optFullscreen = !optFullscreen;
440 if (screenReal == NULL || screenReal->format->BitsPerPixel != 32) {
441 fprintf(stderr, "FATAL: can't switch videomode!\n");
442 exit(1);
444 #else
445 if (screen) { SDL_FreeSurface(screen); screen = NULL; }
446 optFullscreen = !optFullscreen;
447 if (needInit) lvCreateSurfaces();
448 #endif
449 if (needInit) screenFlip();
453 void updateScale (void) {
454 const int needInit = !!screen;
455 if (screen) { SDL_FreeSurface(screen); screen = NULL; }
456 if (needInit) {
457 lvCreateSurfaces();
458 screenFlip();
463 ////////////////////////////////////////////////////////////////////////////////
464 void lockSurface (SurfaceLock *lock, SDL_Surface *s) {
465 if (lock) {
466 lock->s = s;
467 lock->needUnlock = 0;
468 if (s != NULL && SDL_MUSTLOCK(s)) {
469 lock->needUnlock = (SDL_LockSurface(s) == 0);
475 void unlockSurface (SurfaceLock *lock) {
476 if (lock && lock->s && lock->needUnlock) {
477 SDL_UnlockSurface(lock->s);
478 lock->needUnlock = 0;
483 void clearScreen (Uint8 col) {
484 if (col != 255) {
485 Uint32 clr = palette[col];
486 Uint32 *d = (Uint32 *)((Uint8 *)frameSfc->pixels);
488 for (int y = 0; y < frameSfc->h; ++y) {
489 for (int x = 0; x < frameSfc->w; ++x) d[x] = clr;
490 d = (Uint32 *)(((Uint8 *)d)+frameSfc->pitch);
496 ////////////////////////////////////////////////////////////////////////////////
497 // surface must be locked!
498 void drawChar6 (char ch, int x, int y, Uint8 fg, Uint8 bg) {
499 int pos = (ch&0xff)*8;
501 for (int dy = 0; dy < 8; ++dy) {
502 uint8_t b = font6x8[pos++];
504 for (int dx = 0; dx < 6; ++dx) {
505 Uint8 c = (b&0x80 ? fg : bg);
507 if (c != 255) putPixel(x+dx, y+dy, c);
508 b = (b&0x7f)<<1;
514 void drawStr6 (const char *str, int x, int y, Uint8 fg, Uint8 bg) {
515 if (str) {
516 while (*str) {
517 drawChar6(*str++, x, y, fg, bg);
518 x += 6;
524 void drawChar8 (char ch, int x, int y, Uint8 fg, Uint8 bg) {
525 int pos = (ch&0xff)*8;
527 for (int dy = 0; dy < 8; ++dy) {
528 uint8_t b = msxfont8x8[pos++];
530 for (int dx = 0; dx < 8; ++dx) {
531 Uint8 c = (b&0x80 ? fg : bg);
533 if (c != 255) putPixel(x+dx, y+dy, c);
534 b = (b&0x7f)<<1;
540 void drawStr8 (const char *str, int x, int y, Uint8 fg, Uint8 bg) {
541 if (str) {
542 while (*str) {
543 drawChar8(*str++, x, y, fg, bg);
544 x += 8;
550 void drawStr6Outline (const char *str, int x, int y, Uint8 fg, Uint8 oc) {
551 for (int dy = -1; dy < 2; ++dy) {
552 for (int dx = -1; dx < 2; ++dx) {
553 if (dx || dy) drawStr6(str, x+dx, y+dy, oc, 255);
556 drawStr6(str, x, y, fg, 255);
560 void drawStr8Outline (const char *str, int x, int y, Uint8 fg, Uint8 oc) {
561 for (int dy = -1; dy < 2; ++dy) {
562 for (int dx = -1; dx < 2; ++dx) {
563 if (dx || dy) drawStr8(str, x+dx, y+dy, oc, 255);
566 drawStr8(str, x, y, fg, 255);
570 ////////////////////////////////////////////////////////////////////////////////
572 static void drawCharZI (const uint8_t *dta, int hgt, int x, int y, Uint8 c1, Uint8 c2) {
573 int wdt = *dta++, yofs = ((wdt>>4)&0x0f);
575 wdt &= 0x0f;
576 y += yofs;
577 for (int dy = 0; dy < hgt; ++dy) {
578 for (int dx = 0; dx < wdt; dx += 4) {
579 uint8_t b = *dta++;
581 for (int c = 0; c < 4; ++c) {
582 switch ((b>>6)&0x03) {
583 case 0: break;
584 case 1: putPixel(x+dx+c, y+dy, c1); break;
585 case 2: putPixel(x+dx+c, y+dy, c2); break;
586 case 3: break;
588 b = ((b&0x3f)<<2);
595 void drawZFont (void) {
596 const uint8_t *st = zfont;
597 int x = 1, y = 1, hgt = zfont[0];
599 for (int f = 33; f <= 95; ++f) {
600 const uint8_t *st = zfont+(f-32)*2;
601 int ofs = st[0]+256*st[1], w;
603 st = zfont+ofs;
604 w = (st[0]&0x0f);
605 if (x+w > 319) { x = 1; y += hgt+2; }
606 drawCharZI(st, hgt, x, y, 66, 67);
607 x += w;
613 int drawCharZ (char ch, int x, int y, Uint8 c1, Uint8 c2) {
614 if (ch >= 'a' && ch <= 'z') ch -= 32;
615 if (ch < 33 || ch > 95) ch = '?';
616 if (ch != ' ') {
617 const uint8_t *st = zfont+(ch-32)*2;
618 int ofs = st[0]+256*st[1], wdt, yofs, hgt = zfont[0];
620 st = zfont+ofs;
621 wdt = *st++;
622 yofs = ((wdt>>4)&0x0f);
623 wdt &= 0x0f;
624 if (c1 == 255 && c2 == 255) return wdt;
625 y += yofs;
626 while (hgt-- > 0) {
627 int px = x;
629 for (int dx = 0; dx < wdt; dx += 4) {
630 uint8_t b = *st++;
632 for (int c = 0; c < 4; ++c, ++px) {
633 switch ((b>>6)&0x03) {
634 case 0: break;
635 case 1: putPixel(px, y, c1); break;
636 case 2: putPixel(px, y, c2); break;
637 case 3: /*putPixel(x+dx+c, y+dy, 2);*/ break;
639 b = ((b&0x3f)<<2);
642 ++y;
644 return wdt;
645 } else {
646 return zfont[1];
651 int drawStrZ (const char *str, int x, int y, Uint8 c1, Uint8 c2) {
652 int wdt = 0;
654 while (*str) {
655 int w = drawCharZ(*str++, x, y, c1, c2);
657 wdt += w;
658 x += w;
660 return wdt;
664 static inline Uint32 fixColor (Uint32 clr) {
665 if (vidColorMode) {
666 Uint8 *ca = (Uint8 *)&clr;
667 Uint8 r = ca[vidRIdx], g = ca[vidGIdx], b = ca[vidBIdx];
668 //Uint32 lumi = (0.3*((double)r/255.0)+0.59*((double)g/255.0)+0.11*((double)b/255.0))*255.0;
669 Uint32 lumi = (4915*r+9666*g+1802*b);
671 if (vidColorMode > 2) {
672 if (lumi >= 128*16384) lumi /= 16384; else lumi /= 11000;
673 } else {
674 lumi /= 16384;
676 if (lumi > 255) lumi = 255;
678 switch (vidColorMode) {
679 case 1: case 3: // b/w
680 ca[vidRIdx] = ca[vidGIdx] = ca[vidBIdx] = lumi;
681 break;
682 case 2: case 4: // green
683 ca[vidRIdx] = ca[vidBIdx] = 0;
684 ca[vidGIdx] = lumi;
685 break;
688 return clr;
692 ////////////////////////////////////////////////////////////////////////////////
693 static void Blit4xInternal (void) {
694 const Uint32 *scrB = screen->pixels; // 320x240
695 Uint32 *scrR = screenReal->pixels; // 1280x960
696 for (int y = 0; y < screen->h; ++y) {
697 const Uint32 *s = scrB;
698 Uint32 *d0 = scrR;
699 Uint32 *d1 = (Uint32 *)((Uint8 *)d0+screenReal->pitch);
700 Uint32 *d2 = (Uint32 *)((Uint8 *)d1+screenReal->pitch);
701 Uint32 *d3 = (Uint32 *)((Uint8 *)d2+screenReal->pitch);
702 for (int x = 0; x < screen->w; ++x) {
703 const Uint32 clr = fixColor(*s++);
704 d0[0] = d0[1] = d0[2] = d0[3] = clr;
705 d1[0] = d1[1] = d1[2] = d1[3] = clr;
706 d2[0] = d2[1] = d2[2] = d2[3] = clr;
707 d3[0] = d3[1] = d3[2] = d3[3] = clr;
708 d0 += 4;
709 d1 += 4;
710 d2 += 4;
711 d3 += 4;
713 scrB = (Uint32 *)((Uint8 *)scrB+screen->pitch);
714 scrR = (Uint32 *)((Uint8 *)scrR+screenReal->pitch*4);
719 static void TV4xInternal (void) {
720 Uint32 *scrR = screenReal->pixels; // 1280x960
721 scrR = (Uint32 *)((Uint8 *)scrR+screenReal->pitch);
722 for (int y = screenReal->h/4; y--; scrR = (Uint32 *)((Uint8 *)scrR+screenReal->pitch*4)) {
723 Uint32 *d0 = scrR;
724 for (int x = 0; x < screen->w; ++x) {
725 const Uint32 c0 = *d0;
726 Uint32 c1 = (((c0&0x00ff00ff)*7)>>3)&0x00ff00ff;
727 c1 |= (((c0&0x0000ff00)*7)>>3)&0x0000ff00;
728 d0[0] = d0[1] = d0[2] = d0[3] = c1;
729 //d0[0] = d0[1] = d0[2] = d0[3] = 0x00ff0000;
730 d0 += 4;
732 d0 = (Uint32 *)((Uint8 *)scrR+screenReal->pitch);
733 for (int x = 0; x < screen->w; ++x) {
734 Uint32 c0 = *d0;
735 Uint32 c1 = (((c0&0x00ff00ff)*7)>>3)&0x00ff00ff;
736 c1 |= (((c0&0x0000ff00)*7)>>3)&0x0000ff00;
737 d0[0] = d0[1] = d0[2] = d0[3] = c1;
738 #if 0
739 c0 = *d0;
740 c1 = (((c0&0x00ff00ff)*7)>>3)&0x00ff00ff;
741 c1 |= (((c0&0x0000ff00)*7)>>3)&0x0000ff00;
742 d0[0] = d0[1] = d0[2] = d0[3] = c1;
743 #endif
744 //d0[0] = d0[1] = d0[2] = d0[3] = 0x00ff0000;
745 d0 += 4;
751 static void Blit4x (void) {
752 const int needUnlock = (SDL_MUSTLOCK(screenReal) ? SDL_LockSurface(screenReal) == 0 : 0);
753 Blit4xInternal();
754 if (needUnlock) SDL_UnlockSurface(screenReal);
758 static void Blit4xTV (void) {
759 const int needUnlock = (SDL_MUSTLOCK(screenReal) ? SDL_LockSurface(screenReal) == 0 : 0);
760 /*FIXME: make this faster!*/
761 Blit4xInternal();
762 TV4xInternal();
763 if (needUnlock) SDL_UnlockSurface(screenReal);
767 static void Blit3xInternal (void) {
768 const Uint32 *scrB = screen->pixels; // 320x240
769 Uint32 *scrR = screenReal->pixels; // 960x720
770 for (int y = 0; y < screen->h; ++y) {
771 const Uint32 *s = scrB;
772 Uint32 *d0 = scrR;
773 Uint32 *d1 = (Uint32 *)((Uint8 *)d0+screenReal->pitch);
774 Uint32 *d2 = (Uint32 *)((Uint8 *)d1+screenReal->pitch);
775 for (int x = 0; x < screen->w; ++x) {
776 const Uint32 clr = fixColor(*s++);
777 d0[0] = d0[1] = d0[2] = clr;
778 d1[0] = d1[1] = d1[2] = clr;
779 d2[0] = d2[1] = d2[2] = clr;
780 d0 += 3;
781 d1 += 3;
782 d2 += 3;
784 scrB = (Uint32 *)((Uint8 *)scrB+screen->pitch);
785 scrR = (Uint32 *)((Uint8 *)scrR+screenReal->pitch*3);
790 static void TV3xInternal (void) {
791 Uint32 *scrR = screenReal->pixels; // 960x720
792 scrR = (Uint32 *)((Uint8 *)scrR+screenReal->pitch);
793 for (int y = screenReal->h/2; y--; scrR = (Uint32 *)((Uint8 *)scrR+screenReal->pitch*2)) {
794 Uint32 *d0 = scrR;
795 for (int x = 0; x < screen->w; ++x) {
796 const Uint32 c0 = *d0;
797 Uint32 c1 = (((c0&0x00ff00ff)*7)>>3)&0x00ff00ff;
798 c1 |= (((c0&0x0000ff00)*7)>>3)&0x0000ff00;
799 d0[0] = d0[1] = d0[2] = c1;
800 d0 += 3;
806 static void Blit3x (void) {
807 const int needUnlock = (SDL_MUSTLOCK(screenReal) ? SDL_LockSurface(screenReal) == 0 : 0);
808 Blit3xInternal();
809 if (needUnlock) SDL_UnlockSurface(screenReal);
813 static void Blit3xTV (void) {
814 const int needUnlock = (SDL_MUSTLOCK(screenReal) ? SDL_LockSurface(screenReal) == 0 : 0);
815 /*FIXME: make this faster!*/
816 Blit3xInternal();
817 TV3xInternal();
818 if (needUnlock) SDL_UnlockSurface(screenReal);
822 static void Blit2x (void) {
823 const int needUnlock = (SDL_MUSTLOCK(screenReal) ? SDL_LockSurface(screenReal) == 0 : 0);
824 const Uint32 *scrB = screen->pixels; // 320x240
825 Uint32 *scrR = screenReal->pixels; // 640x480
826 for (int y = 0; y < screen->h; ++y) {
827 const Uint32 *s = scrB;
828 Uint32 *d = scrR, *d2 = (Uint32 *)((Uint8 *)d+screenReal->pitch);
829 for (int x = 0; x < screen->w; ++x) {
830 const Uint32 clr = fixColor(*s++);
831 d[0] = d[1] = clr;
832 d2[0] = d2[1] = clr;
833 d += 2; d2 += 2;
834 //++s;
836 scrB = (Uint32 *)((Uint8 *)scrB+screen->pitch);
837 scrR = (Uint32 *)((Uint8 *)scrR+screenReal->pitch*2);
839 if (needUnlock) SDL_UnlockSurface(screenReal);
843 static void Blit2xTV (void) {
844 const int needUnlock = (SDL_MUSTLOCK(screenReal) ? SDL_LockSurface(screenReal) == 0 : 0);
845 const Uint32 *scrB = screen->pixels; // 320x240
846 Uint32 *scrR = screenReal->pixels; // 640x480
847 for (int y = 0; y < screen->h; ++y) {
848 const Uint32 *s = scrB;
849 Uint32 *d = scrR, *d2 = (Uint32 *)((Uint8 *)d+screenReal->pitch);
850 for (int x = 0; x < screen->w; ++x) {
851 const Uint32 c0 = fixColor(*s);
852 Uint32 c1 = (((c0&0x00ff00ff)*7)>>3)&0x00ff00ff;
853 c1 |= (((c0&0x0000ff00)*7)>>3)&0x0000ff00;
854 d[0] = d[1] = c0;
855 d2[0] = d2[1] = c1;
856 d += 2; d2 += 2;
857 ++s;
859 scrB = (Uint32 *)((Uint8 *)scrB+screen->pitch);
860 scrR = (Uint32 *)((Uint8 *)scrR+screenReal->pitch*2);
862 if (needUnlock) SDL_UnlockSurface(screenReal);
866 static void Blit1x (void) {
867 const int needUnlock = (SDL_MUSTLOCK(screenReal) ? SDL_LockSurface(screenReal) == 0 : 0);
868 const Uint32 *scrB = screen->pixels; // 320x240
869 Uint32 *scrR = screenReal->pixels; // 320x240
870 for (int y = 0; y < screen->h; ++y) {
871 const Uint32 *s = scrB;
872 Uint32 *d = scrR;
873 for (int x = 0; x < screen->w; ++x) {
874 const Uint32 clr = fixColor(*s++);
875 d[0] = clr;
876 d += 1;
878 scrB = (Uint32 *)((Uint8 *)scrB+screen->pitch);
879 scrR = (Uint32 *)((Uint8 *)scrR+screenReal->pitch);
881 if (needUnlock) SDL_UnlockSurface(screenReal);
885 static void vidRenderTextScr16x2 (SDL_Surface *sfc) {
886 // 8x16, x2 scaled
887 Uint32 *sp = (Uint32 *)((Uint8 *)sfc->pixels);
888 // move down a little
890 if (sfc->h > 480*2+16*2) sp = (Uint32 *)((Uint8 *)sp+sfc->pitch*16);
891 else if (sfc->h > 480*2) sp = (Uint32 *)((Uint8 *)sp+sfc->pitch*((sfc->h-480)/2));
893 // center horizontally
894 sp += (sfc->w-640*2)/2;
895 // render
896 const uint32_t vhgt =
897 vid_textsrc_height <= 0 ? (uint32_t)VID_TEXT_HEIGHT :
898 vid_textsrc_height > VID_TEXT_HEIGHT ? VID_TEXT_HEIGHT :
899 (uint32_t)vid_textsrc_height;
900 const VidTChar *tp = vid_textscr;
901 for (uint32_t y = 0; y < vhgt; ++y) {
902 Uint32 *d = sp;
903 for (uint32_t x = 0; x < (uint32_t)VID_TEXT_WIDTH; ++x, ++tp, d += 16) {
904 if (tp->attr != 0) {
905 Uint32 bc = palette[(tp->attr>>4)&0x0f];
906 Uint32 fc = palette[tp->attr&0x0f];
907 const uint8_t *cptr = vid_font8x16+16*tp->ch;
908 Uint32 *cdest0 = d;
909 Uint32 *cdest1 = d;
910 cdest1 = (Uint32 *)((Uint8 *)cdest1+sfc->pitch);
911 for (uint32_t dy = 0; dy < 16; ++dy) {
912 uint8_t b = *cptr++;
913 for (uint32_t dx = 0; dx < 8; ++dx) {
914 *cdest0++ = (b&0x80 ? fc : bc);
915 *cdest0++ = (b&0x80 ? fc : bc);
916 *cdest1++ = (b&0x80 ? fc : bc);
917 *cdest1++ = (b&0x80 ? fc : bc);
918 b <<= 1;
920 cdest0 -= 16;
921 cdest0 = (Uint32 *)((Uint8 *)cdest0+sfc->pitch*2);
922 cdest1 -= 16;
923 cdest1 = (Uint32 *)((Uint8 *)cdest1+sfc->pitch*2);
927 sp = (Uint32 *)((Uint8 *)sp+sfc->pitch*(16*2));
932 static void vidRenderTextScr16 (SDL_Surface *sfc) {
933 // 8x16
934 Uint32 *sp = (Uint32 *)((Uint8 *)sfc->pixels);
935 // move down a little
937 if (sfc->h > 480+16*2) sp = (Uint32 *)((Uint8 *)sp+sfc->pitch*16);
938 else if (sfc->h > 480) sp = (Uint32 *)((Uint8 *)sp+sfc->pitch*((sfc->h-480)/2));
940 // center horizontally
941 sp += (sfc->w-640)/2;
942 // render
943 const uint32_t vhgt =
944 vid_textsrc_height <= 0 ? (uint32_t)VID_TEXT_HEIGHT :
945 vid_textsrc_height > VID_TEXT_HEIGHT ? VID_TEXT_HEIGHT :
946 (uint32_t)vid_textsrc_height;
947 const VidTChar *tp = vid_textscr;
948 for (uint32_t y = 0; y < vhgt; ++y) {
949 Uint32 *d = sp;
950 for (uint32_t x = 0; x < (uint32_t)VID_TEXT_WIDTH; ++x, ++tp, d += 8) {
951 if (tp->attr != 0) {
952 Uint32 bc = palette[(tp->attr>>4)&0x0f];
953 Uint32 fc = palette[tp->attr&0x0f];
954 const uint8_t *cptr = vid_font8x16+16*tp->ch;
955 Uint32 *cdest = d;
956 for (uint32_t dy = 0; dy < 16; ++dy) {
957 uint8_t b = *cptr++;
958 for (uint32_t dx = 0; dx < 8; ++dx) {
959 *cdest++ = (b&0x80 ? fc : bc);
960 b <<= 1;
962 cdest -= 8;
963 cdest = (Uint32 *)((Uint8 *)cdest+sfc->pitch);
967 sp = (Uint32 *)((Uint8 *)sp+sfc->pitch*16);
972 static void vidRenderTextScr8 (SDL_Surface *sfc) {
973 // 8x8
974 Uint32 *sp = (Uint32 *)((Uint8 *)sfc->pixels);
975 // move down a little
977 if (sfc->h > 240+8*2) sp = (Uint32 *)((Uint8 *)sp+sfc->pitch*8);
978 else if (sfc->h > 240) sp = (Uint32 *)((Uint8 *)sp+sfc->pitch*((sfc->h-240)/2));
980 // center horizontally
981 sp += (sfc->w-320)/2;
982 // render
983 const uint32_t vhgt =
984 vid_textsrc_height <= 0 ? (uint32_t)VID_TEXT_HEIGHT :
985 vid_textsrc_height > VID_TEXT_HEIGHT ? VID_TEXT_HEIGHT :
986 (uint32_t)vid_textsrc_height;
987 const VidTChar *tp = vid_textscr;
988 for (uint32_t y = 0; y < vhgt; ++y) {
989 Uint32 *d = sp;
990 for (uint32_t x = 0; x < (uint32_t)VID_TEXT_WIDTH; ++x, ++tp, d += 8) {
991 if (tp->attr != 0) {
992 Uint32 bc = palette[(tp->attr>>4)&0x0f];
993 Uint32 fc = palette[tp->attr&0x0f];
994 const uint8_t *cptr = vid_font8x8+8*tp->ch;
995 Uint32 *cdest = d;
996 for (uint32_t dy = 0; dy < 8; ++dy) {
997 uint8_t b = *cptr++;
998 for (uint32_t dx = 0; dx < 8; ++dx) {
999 *cdest++ = (b&0x80 ? fc : bc);
1000 b <<= 1;
1002 cdest -= 8;
1003 cdest = (Uint32 *)((Uint8 *)cdest+sfc->pitch);
1007 sp = (Uint32 *)((Uint8 *)sp+sfc->pitch*8);
1012 static void vidRenderTextScr (SDL_Surface *sfc) {
1013 if (sfc->w >= 320*4 && sfc->h >= 240*4) vidRenderTextScr16x2(sfc);
1014 else if (sfc->w >= 320*2 && sfc->h >= 240*2) vidRenderTextScr16(sfc);
1015 else vidRenderTextScr8(sfc);
1019 static void screenFlip (void) {
1021 lockSurface(&frameSfcLock, screen);
1022 drawZFont();
1023 unlockSurface(&frameSfcLock);
1026 if (screenReal != NULL) {
1028 if (!optFullscreen) {
1029 if (optTVScaler) Blit3xTV(); else Blit3x();
1030 } else {
1031 if (optTVScaler > 1 || (optTVScaler && !optFullscreen)) Blit2xTV(); else Blit2x();
1034 switch (actualScale) {
1035 case 1: Blit1x(); break;
1036 case 2: if (optTVScaler) Blit2xTV(); else Blit2x(); break;
1037 case 3: if (optTVScaler) Blit3xTV(); else Blit3x(); break;
1038 case 4: if (optTVScaler) Blit4xTV(); else Blit4x(); break;
1039 default: __builtin_trap();
1042 if (vidBeforeFlipCB) {
1043 const int needUnlock = (SDL_MUSTLOCK(screenReal) ? SDL_LockSurface(screenReal) == 0 : 0);
1044 vidBeforeFlipCB(screenReal);
1045 if (needUnlock) SDL_UnlockSurface(screenReal);
1048 if (vid_textscr_active) {
1049 const int needUnlock = (SDL_MUSTLOCK(screenReal) ? SDL_LockSurface(screenReal) == 0 : 0);
1050 vidRenderTextScr(screenReal);
1051 if (needUnlock) SDL_UnlockSurface(screenReal);
1054 SDL_Flip(screenReal);
1055 } else {
1056 if (vidBeforeFlipCB) {
1057 const int needUnlock = (SDL_MUSTLOCK(screen) ? SDL_LockSurface(screen) == 0 : 0);
1058 vidBeforeFlipCB(screen);
1059 if (needUnlock) SDL_UnlockSurface(screen);
1062 if (vid_textscr_active) {
1063 const int needUnlock = (SDL_MUSTLOCK(screen) ? SDL_LockSurface(screen) == 0 : 0);
1064 vidRenderTextScr(screen);
1065 if (needUnlock) SDL_UnlockSurface(screen);
1068 SDL_Flip(screen);
1073 ////////////////////////////////////////////////////////////////////////////////
1074 void forcedShowFrame (void) {
1075 if (frameSfc != NULL) {
1076 frameSfcRaped = 1;
1077 unlockSurface(&frameSfcLock);
1078 screenFlip();
1079 lockSurface(&frameSfcLock, screen);
1084 void buildFrame (void) {
1085 frameSfcRaped = 0;
1086 if (frameCB != NULL) {
1087 lockSurface(&frameSfcLock, screen);
1088 frameSfc = screen;
1089 frameCB();
1090 frameSfc = NULL;
1091 unlockSurface(&frameSfcLock);
1093 if (!frameSfcRaped) screenFlip();
1097 static Uint32 cbFrameTimer (Uint32 interval, void *param) {
1098 SDL_Event evt;
1100 evt.type = SDL_USEREVENT;
1102 //evt.user.type = SDL_USEREVENT;
1103 evt.user.code = 0;
1104 evt.user.data1 = NULL;
1105 evt.user.data2 = NULL;
1107 SDL_PushEvent(&evt);
1108 //fprintf(stderr, "!\n");
1109 return interval;
1113 ////////////////////////////////////////////////////////////////////////////////
1114 void postQuitMessage (void) {
1115 SDL_Event evt;
1117 evt.type = SDL_QUIT;
1118 SDL_PushEvent(&evt);
1122 ////////////////////////////////////////////////////////////////////////////////
1123 int msButtons = 0, msLastX = 0, msLastY = 0, kbCtrl = 0, kbAlt = 0, kbShift = 0;
1124 int kbLCtrl = 0, kbRCtrl = 0, kbLAlt = 0, kbRAlt = 0, kbLShift = 0, kbRShift = 0;
1125 int msAutoTimeMSFirst = 500;
1126 int msAutoTimeMSRepeat = 100;
1127 int msAutoTresholdX = 2;
1128 int msAutoTresholdY = 2;
1131 typedef struct {
1132 int64_t time;
1133 int x, y;
1134 int repeat;
1135 } MSPressInfo;
1137 static MSPressInfo msbi[5]; // left,right,middle
1140 typedef struct {
1141 SDLKey key;
1142 int *var;
1143 } KBCtrlKeyInfo;
1146 static const KBCtrlKeyInfo kbCtrlKeys[] = {
1147 {.key=SDLK_LCTRL, .var=&kbLCtrl},
1148 {.key=SDLK_RCTRL, .var=&kbRCtrl},
1149 {.key=SDLK_LALT, .var=&kbLAlt},
1150 {.key=SDLK_RALT, .var=&kbRAlt},
1151 {.key=SDLK_LSHIFT, .var=&kbLShift},
1152 {.key=SDLK_RSHIFT, .var=&kbRShift},
1156 static void initEventSystem (void) {
1157 memset(msbi, 0, sizeof(msbi));
1161 static void fixKbCtrl (SDLKey key, int isdown) {
1162 for (unsigned int f = 0; f < sizeof(kbCtrlKeys)/sizeof(KBCtrlKeyInfo); ++f) {
1163 if (key == kbCtrlKeys[f].key) *(kbCtrlKeys[f].var) = isdown;
1165 kbCtrl = kbLCtrl||kbRCtrl;
1166 kbAlt = kbLAlt||kbRAlt;
1167 kbShift = kbLShift||kbRShift;
1171 static void kbDepressCtrls (void) {
1172 for (unsigned int f = 0; f < sizeof(kbCtrlKeys)/sizeof(KBCtrlKeyInfo); ++f) {
1173 if (*(kbCtrlKeys[f].var)) {
1174 fixKbCtrl(kbCtrlKeys[f].key, 0);
1175 if (keyCB) {
1176 SDL_Event event;
1178 event.type = SDL_KEYUP;
1179 event.key.state = SDL_RELEASED;
1180 event.key.keysym.scancode = 0;
1181 event.key.keysym.unicode = 0;
1182 event.key.keysym.sym = kbCtrlKeys[f].key;
1183 event.key.keysym.mod =
1184 (kbLCtrl ? KMOD_LCTRL : 0) |
1185 (kbRCtrl ? KMOD_RCTRL : 0) |
1186 (kbLAlt ? KMOD_LALT : 0) |
1187 (kbRAlt ? KMOD_RALT : 0) |
1188 (kbLShift ? KMOD_LSHIFT : 0) |
1189 (kbRShift ? KMOD_RSHIFT : 0) |
1191 keyCB(&event.key);
1198 static void doAutoMouse (void) {
1199 if (mouseButtonCB && msAutoTimeMSFirst > 0) {
1200 int64_t t = timerGetMS();
1202 for (int b = 0; b < 3; ++b) {
1203 int n = (1<<b);
1205 if (msbi[n].time > 0) {
1206 if (!msbi[n].repeat && (abs(msLastX-msbi[n].x) > msAutoTresholdX || abs(msLastY-msbi[n].y) > msAutoTresholdY)) {
1207 // mouse moved too far, stop autorepeating
1208 msbi[n].time = 0;
1209 } else if ((msbi[n].repeat && t-msbi[n].time >= msAutoTimeMSRepeat) ||
1210 (!msbi[n].repeat && t-msbi[n].time >= msAutoTimeMSFirst)) {
1211 // generate repeat
1212 msbi[n].time = t;
1213 msbi[n].repeat = 1;
1214 mouseButtonCB(msLastX, msLastY, (n|MS_BUTTON_AUTO), msButtons);
1222 // -1: quit; 1: new frame
1223 int processEvents (int dowait) {
1224 SDL_Event event;
1226 while ((dowait ? SDL_WaitEvent(&event) : SDL_PollEvent(&event))) {
1227 switch (event.type) {
1228 case SDL_QUIT: // the user want to quit
1229 return -1;
1230 case SDL_KEYDOWN:
1231 case SDL_KEYUP:
1232 fixKbCtrl(event.key.keysym.sym, (event.type == SDL_KEYDOWN));
1233 if (keyCB) keyCB(&event.key);
1234 break;
1235 case SDL_MOUSEMOTION: {
1236 //int buttons;
1237 int x = event.motion.x, y = event.motion.y, rx = event.motion.xrel, ry = event.motion.yrel;
1239 if (screenReal != NULL) { x /= 2; y /= 2; }
1240 msLastX = x;
1241 msLastY = y;
1242 //SDLMB2VMB(event.motion.state);
1243 //msButtons = buttons;
1244 if (mouseCB) mouseCB(x, y, rx, ry, msButtons);
1245 break; }
1246 case SDL_MOUSEBUTTONUP:
1247 case SDL_MOUSEBUTTONDOWN: {
1248 int x = event.button.x, y = event.button.y, btn = 0;
1250 if (screenReal != NULL) { x /= 2; y /= 2; }
1251 msLastX = x;
1252 msLastY = y;
1253 switch (event.button.button) {
1254 case SDL_BUTTON_LEFT: btn = MS_BUTTON_LEFT; break;
1255 case SDL_BUTTON_RIGHT: btn = MS_BUTTON_RIGHT; break;
1256 case SDL_BUTTON_MIDDLE: btn = MS_BUTTON_MIDDLE; break;
1257 case SDL_BUTTON_WHEELUP: btn = MS_BUTTON_WHEELUP; break;
1258 case SDL_BUTTON_WHEELDOWN: btn = MS_BUTTON_WHEELDOWN; break;
1259 default: ;
1261 //fprintf(stderr, "DOWN:%d; btn:%d; bt:%02x\n", (event.button.state == SDL_PRESSED), btn, msButtons);
1262 if (btn != 0) {
1263 if (event.button.state == SDL_PRESSED) {
1264 msButtons |= btn;
1265 if (btn <= MS_BUTTON_MIDDLE) {
1266 msbi[btn].time = timerGetMS();
1267 msbi[btn].x = x;
1268 msbi[btn].y = y;
1269 msbi[btn].repeat = 0;
1271 } else {
1272 msButtons &= ~btn;
1273 btn |= MS_BUTTON_DEPRESSED;
1274 if (btn <= MS_BUTTON_MIDDLE) msbi[btn].time = 0;
1276 if (mouseButtonCB) mouseButtonCB(x, y, btn, msButtons);
1278 break; }
1279 case SDL_ACTIVEEVENT:
1280 if (event.active.gain) {
1281 vidWindowActivated = 1;
1282 if (windowActivationCB != NULL) windowActivationCB(1);
1283 } else {
1284 vidWindowActivated = 0;
1285 SDL_WM_GrabInput(SDL_GRAB_OFF);
1286 // just in case
1287 kbDepressCtrls();
1288 if (msButtons != 0) {
1289 if (mouseButtonCB != NULL) {
1290 for (unsigned int mask = 0x01; mask < MS_BUTTON_DEPRESSED; mask <<= 1) {
1291 if (msButtons&mask) {
1292 msButtons &= ~mask;
1293 mouseButtonCB(msLastX, msLastY, mask|MS_BUTTON_DEPRESSED, msButtons);
1297 msButtons = 0;
1299 if (windowActivationCB != NULL) windowActivationCB(0);
1301 break;
1302 case SDL_USEREVENT:
1303 //while (SDL_PollEvent(NULL)) processEvents();
1304 doAutoMouse();
1305 return 1;
1306 default: ;
1309 doAutoMouse();
1310 return 0;
1314 ////////////////////////////////////////////////////////////////////////////////
1315 void mainLoop (void) {
1316 SDL_TimerID frameTimer = SDL_AddTimer(20, cbFrameTimer, NULL);
1317 int eres = 1;
1319 while (eres >= 0) {
1320 if (eres > 0) buildFrame();
1321 eres = processEvents(1);
1324 SDL_RemoveTimer(frameTimer);
1328 ////////////////////////////////////////////////////////////////////////////////
1329 VOverlay *createVO (int w, int h) {
1330 if (w > 0 && h > 0) {
1331 VOverlay *res = malloc(sizeof(VOverlay));
1332 if (res != NULL) {
1333 if ((res->data = malloc(w*h)) != NULL) {
1334 res->w = w;
1335 res->h = h;
1336 return res;
1338 free(res);
1341 return NULL;
1345 void resizeVO (VOverlay *v, int w, int h) {
1346 if (v && w > 0 && h > 0 && (v->w != w || v->h != h)) {
1347 v->data = realloc(v->data, w*h);
1348 if (!v->data) { fprintf(stderr, "\bFATAL: out of memory!\n"); abort(); }
1349 v->w = w;
1350 v->h = h;
1355 VOverlay *createVOFromIcon (const void *iconbuf) {
1356 if (iconbuf != NULL) {
1357 const uint8_t *ibuf = (const uint8_t *)iconbuf;
1358 VOverlay *res = createVO(ibuf[0], ibuf[1]);
1360 if (res != NULL) {
1361 clearVO(res, 255);
1362 for (int y = 0; y < ibuf[1]; ++y) {
1363 for (int x = 0; x < ibuf[0]; ++x) {
1364 putPixelVO(res, x, y, ibuf[2+y*ibuf[0]+x]);
1367 return res;
1370 return NULL;
1374 void freeVO (VOverlay *v) {
1375 if (v != NULL) {
1376 if (v->data != NULL) free(v->data);
1377 free(v);
1381 void clearVO (VOverlay *v, Uint8 c) {
1382 if (v != NULL && v->w > 0 && v->h > 0) memset(v->data, c, v->w*v->h);
1386 ////////////////////////////////////////////////////////////////////////////////
1387 void drawChar6VO (VOverlay *v, char ch, int x, int y, Uint8 fg, Uint8 bg) {
1388 if (v != NULL && x > -6 && y > -8 && x < v->w+6 && y < v->h+8) {
1389 int pos = (ch&0xff)*8;
1391 for (int dy = 0; dy < 8; ++dy, ++y) {
1392 uint8_t b = font6x8[pos++];
1394 for (int dx = 0; dx < 6; ++dx) {
1395 Uint8 c = (b&0x80 ? fg : bg);
1397 if (c != 255) putPixelVO(v, x+dx, y, c);
1398 b = (b&0x7f)<<1;
1405 void drawStr6VO (VOverlay *v, const char *str, int x, int y, Uint8 fg, Uint8 bg) {
1406 if (str != NULL) for (; *str; ++str, x += 6) drawChar6VO(v, *str, x, y, fg, bg);
1410 void drawChar8VO (VOverlay *v, char ch, int x, int y, Uint8 fg, Uint8 bg) {
1411 if (v != NULL && x > -8 && y > -8 && x < v->w+8 && y < v->h+8) {
1412 int pos = (ch&0xff)*8;
1414 for (int dy = 0; dy < 8; ++dy, ++y) {
1415 uint8_t b = msxfont8x8[pos++];
1417 for (int dx = 0; dx < 8; ++dx) {
1418 Uint8 c = (b&0x80 ? fg : bg);
1420 if (c != 255) putPixelVO(v, x+dx, y, c);
1421 b = (b&0x7f)<<1;
1428 void drawStr8VO (VOverlay *v, const char *str, int x, int y, Uint8 fg, Uint8 bg) {
1429 if (str != NULL) for (; *str; ++str, x += 8) drawChar8VO(v, *str, x, y, fg, bg);
1433 void drawFrameVO (VOverlay *v, int x0, int y0, int w, int h, Uint8 c) {
1434 if (v != NULL && w > 0 && h > 0 && x0 > -w && y0 > -h && x0 < v->w && y0 < v->h) {
1435 if (x0 < 0) { if ((w += x0) < 1) return; x0 = 0; }
1436 if (y0 < 0) { if ((h += y0) < 1) return; y0 = 0; }
1437 if (x0+w > v->w) { if ((w = v->w-x0) < 1) return; }
1438 if (y0+h > v->h) { if ((h = v->h-y0) < 1) return; }
1440 memset(v->data+y0*v->w+x0, c, w);
1441 if (h > 1) memset(v->data+(y0+h-1)*v->w+x0, c, w);
1442 if (h > 2) {
1443 --w;
1444 h -= 2;
1445 for (uint8_t *d = v->data+(y0+1)*v->w+x0; h > 0; --h, d += v->w) d[0] = d[w] = c;
1451 void fillRectVO (VOverlay *v, int x0, int y0, int w, int h, Uint8 c) {
1452 if (v != NULL && w > 0 && h > 0 && x0 > -w && y0 > -h && x0 < v->w && y0 < v->h) {
1453 if (x0 < 0) { if ((w += x0) < 1) return; x0 = 0; }
1454 if (y0 < 0) { if ((h += y0) < 1) return; y0 = 0; }
1455 if (x0+w > v->w) { if ((w = v->w-x0) < 1) return; }
1456 if (y0+h > v->h) { if ((h = v->h-y0) < 1) return; }
1458 for (uint8_t *d = v->data+y0*v->w+x0; h > 0; --h, d += v->w) memset(d, c, w);
1463 ////////////////////////////////////////////////////////////////////////////////
1464 void blitVO (VOverlay *v, int x0, int y0, Uint8 alpha) {
1465 if (v != NULL && x0 > -v->w && y0 > -v->h && x0 < frameSfc->w && y0 < frameSfc->h && alpha > 0) {
1466 int w = v->w, h = v->h;
1467 const uint8_t *vs = v->data;
1468 Uint8 *dest;
1470 if (y0 < 0) {
1471 // skip invisible top part
1472 if ((h += y0) < 1) return;
1473 vs -= y0*v->w;
1474 y0 = 0;
1476 if (y0+h > frameSfc->h) {
1477 // don't draw invisible bottom part
1478 if ((h = frameSfc->h-y0) < 1) return;
1481 if (x0 < 0) {
1482 // skip invisible left part
1483 if ((w += x0) < 1) return;
1484 vs -= x0;
1485 x0 = 0;
1487 if (x0+w > frameSfc->w) {
1488 // don't draw invisible right part
1489 if ((w = frameSfc->w-x0) < 1) return;
1492 dest = (((Uint8 *)frameSfc->pixels)+(y0*frameSfc->pitch)+x0*4);
1493 if (alpha == 255) {
1494 for (; h > 0; --h, dest += frameSfc->pitch, vs += v->w) {
1495 Uint32 *d = (Uint32 *)dest;
1496 const uint8_t *s = vs;
1498 for (int dx = w; dx > 0; --dx, ++d, ++s) if (*s != 255) *d = palette[*s];
1500 } else {
1501 for (; h > 0; --h, dest += frameSfc->pitch, vs += v->w) {
1502 Uint8 *d = dest;
1503 const uint8_t *s = vs;
1505 for (int dx = w; dx > 0; --dx, d += 4, ++s) {
1506 uint8_t c;
1508 if ((c = *s) != 255) {
1509 uint8_t r = d[vidRIdx], g = d[vidGIdx], b = d[vidBIdx];
1511 d[vidRIdx] = (((palRGB[c][0]-r)*alpha)>>8)+r;
1512 d[vidGIdx] = (((palRGB[c][1]-g)*alpha)>>8)+g;
1513 d[vidBIdx] = (((palRGB[c][2]-b)*alpha)>>8)+b;
1522 ////////////////////////////////////////////////////////////////////////////////
1523 #include "videoex.c"