[BACK]Return to sdl2-platform.c CVS log [TXT][DIR] Up to [contributed] / brogue-ce / src / platform

Annotation of brogue-ce/src/platform/sdl2-platform.c, Revision 1.1.1.1

1.1       rubenllo    1: #ifdef SDL_PATHS
                      2: #include <unistd.h>
                      3: #endif
                      4:
                      5: #include <limits.h>
                      6: #include <SDL_image.h>
                      7: #include "platform.h"
                      8: #include "tiles.h"
                      9:
                     10: #define PAUSE_BETWEEN_EVENT_POLLING     36L//17
                     11: #define MAX_REMAPS  128
                     12:
                     13: struct keypair {
                     14:     char from;
                     15:     char to;
                     16: };
                     17:
                     18: static struct keypair remapping[MAX_REMAPS];
                     19: static size_t nremaps = 0;
                     20: static boolean showGraphics = false;
                     21:
                     22: static rogueEvent lastEvent;
                     23:
                     24:
                     25: static void sdlfatal(char *file, int line) {
                     26:     fprintf(stderr, "Fatal SDL error (%s:%d): %s\n", file, line, SDL_GetError());
                     27:     exit(1);
                     28: }
                     29:
                     30:
                     31: static void imgfatal(char *file, int line) {
                     32:     fprintf(stderr, "Fatal SDL_image error (%s:%d): %s\n", file, line, IMG_GetError());
                     33:     exit(1);
                     34: }
                     35:
                     36:
                     37: /*
                     38: If the key is to be processed, returns true and updates event. False
                     39: otherwise. This function only listens for keypresses which do not produce
                     40: corresponding TextInputEvents.
                     41: */
                     42: static boolean eventFromKey(rogueEvent *event, SDL_Keycode key) {
                     43:     event->param1 = -1;
                     44:
                     45:     switch (key) {
                     46:         case SDLK_ESCAPE:
                     47:             event->param1 = ESCAPE_KEY;
                     48:             return true;
                     49:         case SDLK_UP:
                     50:             event->param1 = UP_ARROW;
                     51:             return true;
                     52:         case SDLK_DOWN:
                     53:             event->param1 = DOWN_ARROW;
                     54:             return true;
                     55:         case SDLK_RIGHT:
                     56:             event->param1 = RIGHT_ARROW;
                     57:             return true;
                     58:         case SDLK_LEFT:
                     59:             event->param1 = LEFT_ARROW;
                     60:             return true;
                     61:         case SDLK_RETURN:
                     62:         case SDLK_KP_ENTER:
                     63:             event->param1 = RETURN_KEY;
                     64:             return true;
                     65:         case SDLK_BACKSPACE:
                     66:             event->param1 = DELETE_KEY;
                     67:             return true;
                     68:         case SDLK_TAB:
                     69:             event->param1 = TAB_KEY;
                     70:             return true;
                     71:         case SDLK_PRINTSCREEN:
                     72:             event->param1 = PRINTSCREEN_KEY;
                     73:             return true;
                     74:     }
                     75:
                     76:     /*
                     77:     Only process keypad events when we're holding a modifier, as there is no
                     78:     TextInputEvent then.
                     79:     */
                     80:     if (event->shiftKey || event->controlKey) {
                     81:         switch (key) {
                     82:             case SDLK_KP_0:
                     83:                 event->param1 = NUMPAD_0;
                     84:                 return true;
                     85:             case SDLK_KP_1:
                     86:                 event->param1 = NUMPAD_1;
                     87:                 return true;
                     88:             case SDLK_KP_2:
                     89:                 event->param1 = NUMPAD_2;
                     90:                 return true;
                     91:             case SDLK_KP_3:
                     92:                 event->param1 = NUMPAD_3;
                     93:                 return true;
                     94:             case SDLK_KP_4:
                     95:                 event->param1 = NUMPAD_4;
                     96:                 return true;
                     97:             case SDLK_KP_5:
                     98:                 event->param1 = NUMPAD_5;
                     99:                 return true;
                    100:             case SDLK_KP_6:
                    101:                 event->param1 = NUMPAD_6;
                    102:                 return true;
                    103:             case SDLK_KP_7:
                    104:                 event->param1 = NUMPAD_7;
                    105:                 return true;
                    106:             case SDLK_KP_8:
                    107:                 event->param1 = NUMPAD_8;
                    108:                 return true;
                    109:             case SDLK_KP_9:
                    110:                 event->param1 = NUMPAD_9;
                    111:                 return true;
                    112:         }
                    113:     }
                    114:
                    115:     // Ctrl+letter doesn't give a TextInputEvent
                    116:     if (event->controlKey && key >= SDLK_a && key <= SDLK_z) {
                    117:         event->param1 = 'a' + (key - SDLK_a);
                    118:         if (event->shiftKey) event->param1 -= 'a' - 'A';
                    119:         return true;
                    120:     }
                    121:
                    122:     return false;
                    123: }
                    124:
                    125:
                    126: static boolean _modifierHeld(int mod) {
                    127:     SDL_Keymod km = SDL_GetModState();
                    128:     return mod == 0 && (km & (KMOD_LSHIFT | KMOD_RSHIFT))
                    129:         || mod == 1 && (km & (KMOD_LCTRL | KMOD_RCTRL));
                    130: }
                    131:
                    132:
                    133: static char applyRemaps(char c) {
                    134:     for (size_t i=0; i < nremaps; i++) {
                    135:         if (remapping[i].from == c) return remapping[i].to;
                    136:     }
                    137:     return c;
                    138: }
                    139:
                    140:
                    141: /*
                    142: If an event is available, returns true and updates returnEvent. Otherwise
                    143: it returns false and an error event. This function also processes
                    144: platform-specific inputs/behaviours.
                    145: */
                    146: static boolean pollBrogueEvent(rogueEvent *returnEvent, boolean textInput) {
                    147:     static int mx = 0, my = 0;
                    148:
                    149:     returnEvent->eventType = EVENT_ERROR;
                    150:     returnEvent->shiftKey = _modifierHeld(0);
                    151:     returnEvent->controlKey = _modifierHeld(1);
                    152:
                    153:     SDL_Event event;
                    154:     boolean ret = false;
                    155:
                    156:
                    157:     // ~ for (int i=0; i < 100 && SDL_PollEvent(&event); i++) {
                    158:     while (SDL_PollEvent(&event)) {
                    159:         if (event.type == SDL_QUIT) {
                    160:             // the player clicked the X button!
                    161:             SDL_Quit();
                    162:             quitImmediately();
                    163:         } else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
                    164:             resizeWindow(event.window.data1, event.window.data2);
                    165:         } else if (event.type == SDL_KEYDOWN) {
                    166:             SDL_Keycode key = event.key.keysym.sym;
                    167:
                    168:             if (key == SDLK_PAGEUP) {
                    169:                 resizeWindow(max(windowWidth * 11/10, windowWidth + 1), max(windowHeight * 11/10, windowHeight + 1));
                    170:                 continue;
                    171:             } else if (key == SDLK_PAGEDOWN) {
                    172:                 fullScreen = false;
                    173:                 resizeWindow(max(windowWidth * 10/11, 1), max(windowHeight * 10/11, 1));
                    174:                 continue;
                    175:             } else if (key == SDLK_F11 || key == SDLK_F12
                    176:                     || key == SDLK_RETURN && (SDL_GetModState() & KMOD_ALT)) {
                    177:                 fullScreen = !fullScreen;
                    178:                 resizeWindow(-1, -1);  // Reset to starting resolution
                    179:                 continue;
                    180:             }
                    181:
                    182:             if (eventFromKey(returnEvent, key)) {
                    183:                 returnEvent->eventType = KEYSTROKE;
                    184:                 return true;
                    185:             }
                    186:         } else if (event.type == SDL_TEXTINPUT && (unsigned char)(event.text.text[0]) < 0x80) {
                    187:             /*
                    188:             It's difficult/impossible to check what characters are on the
                    189:             shifts of keys. So to detect '&', '>' etc. reliably we need to
                    190:             listen for text input events as well as keydowns. This results
                    191:             in hybrid keyboard code, where Brogue KEYSTROKEs can come from
                    192:             different SDL events.
                    193:             */
                    194:             char c = event.text.text[0];
                    195:
                    196:             if (!textInput) {
                    197:                 c = applyRemaps(c);
                    198:                 if (c == '=' || c == '+') {
                    199:                     resizeWindow(max(windowWidth * 11/10, windowWidth + 1), max(windowHeight * 11/10, windowHeight + 1));
                    200:                 } else if (c == '-') {
                    201:                     fullScreen = false;
                    202:                     resizeWindow(max(windowWidth * 10/11, 1), max(windowHeight * 10/11, 1));
                    203:                 }
                    204:             }
                    205:
                    206:             returnEvent->eventType = KEYSTROKE;
                    207:             returnEvent->param1 = c;
                    208:             // ~ printf("textinput %s\n", event.text.text);
                    209:             return true;
                    210:         } else if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
                    211:             if (event.button.button == SDL_BUTTON_LEFT || event.button.button == SDL_BUTTON_RIGHT) {
                    212:                 if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) {
                    213:                     returnEvent->eventType = MOUSE_DOWN;
                    214:                 } else if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT) {
                    215:                     returnEvent->eventType = MOUSE_UP;
                    216:                 } else if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_RIGHT) {
                    217:                     returnEvent->eventType = RIGHT_MOUSE_DOWN;
                    218:                 } else if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_RIGHT) {
                    219:                     returnEvent->eventType = RIGHT_MOUSE_UP;
                    220:                 }
                    221:                 returnEvent->param1 = event.button.x * COLS / windowWidth;
                    222:                 returnEvent->param2 = event.button.y * ROWS / windowHeight;
                    223:                 return true;
                    224:             }
                    225:         } else if (event.type == SDL_MOUSEMOTION) {
                    226:             // We don't want to return on a mouse motion event, because only the last
                    227:             // in the queue is important. That's why we just set ret=true
                    228:             int xcell = event.motion.x * COLS / windowWidth,
                    229:                 ycell = event.motion.y * ROWS / windowHeight;
                    230:             if (xcell != mx || ycell != my) {
                    231:                 returnEvent->eventType = MOUSE_ENTERED_CELL;
                    232:                 returnEvent->param1 = xcell;
                    233:                 returnEvent->param2 = ycell;
                    234:                 mx = xcell;
                    235:                 my = ycell;
                    236:                 ret = true;
                    237:             }
                    238:         }
                    239:     }
                    240:
                    241:     return ret;
                    242: }
                    243:
                    244:
                    245: static void _gameLoop() {
                    246: #ifdef SDL_PATHS
                    247:     char *path = SDL_GetBasePath();
                    248:     if (path) {
                    249:         path[strlen(path) - 1] = '\0';  // remove trailing separator
                    250:         strcpy(dataDirectory, path);
                    251:     } else {
                    252:         fprintf(stderr, "Failed to find the path to the application\n");
                    253:         exit(1);
                    254:     }
                    255:     free(path);
                    256:
                    257:     path = SDL_GetPrefPath("Brogue", "Brogue CE");
                    258:     if (!path || chdir(path) != 0) {
                    259:         fprintf(stderr, "Failed to find or change to the save directory\n");
                    260:         exit(1);
                    261:     }
                    262:     free(path);
                    263: #endif
                    264:
                    265:     if (SDL_Init(SDL_INIT_VIDEO) < 0) sdlfatal(__FILE__, __LINE__);
                    266:
                    267:     if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG)) imgfatal(__FILE__, __LINE__);
                    268:
                    269:     initTiles();
                    270:
                    271:     lastEvent.eventType = EVENT_ERROR;
                    272:
                    273:     resizeWindow(windowWidth, windowHeight);
                    274:
                    275:     rogueMain();
                    276:
                    277:     SDL_Quit();
                    278: }
                    279:
                    280:
                    281: static boolean _pauseForMilliseconds(short ms) {
                    282:     updateScreen();
                    283:     SDL_Delay(ms);
                    284:
                    285:     if (lastEvent.eventType != EVENT_ERROR
                    286:         && lastEvent.eventType != MOUSE_ENTERED_CELL) {
                    287:         return true; // SDL already gave us an interrupting event to process
                    288:     }
                    289:
                    290:     return pollBrogueEvent(&lastEvent, false) // ask SDL for a new event if one is available
                    291:         && lastEvent.eventType != EVENT_ERROR // and check if it is interrupting
                    292:         && lastEvent.eventType != MOUSE_ENTERED_CELL;
                    293: }
                    294:
                    295:
                    296: static void _nextKeyOrMouseEvent(rogueEvent *returnEvent, boolean textInput, boolean colorsDance) {
                    297:     long tstart, dt;
                    298:
                    299:     updateScreen();
                    300:
                    301:     if (lastEvent.eventType != EVENT_ERROR) {
                    302:         *returnEvent = lastEvent;
                    303:         lastEvent.eventType = EVENT_ERROR;
                    304:         return;
                    305:     }
                    306:
                    307:     while (true) {
                    308:         tstart = SDL_GetTicks();
                    309:
                    310:         if (colorsDance) {
                    311:             shuffleTerrainColors(3, true);
                    312:             commitDraws();
                    313:         }
                    314:
                    315:         updateScreen();
                    316:
                    317:         if (pollBrogueEvent(returnEvent, textInput)) break;
                    318:
                    319:         dt = PAUSE_BETWEEN_EVENT_POLLING - (SDL_GetTicks() - tstart);
                    320:         if (dt > 0) {
                    321:             SDL_Delay(dt);
                    322:         }
                    323:     }
                    324: }
                    325:
                    326:
                    327: /*
                    328: Returns the index of the sprite representing the given glyph. Sprites <256 are
                    329: from the text font sheet, 256+ are from the tiles sheet.
                    330: */
                    331: static int fontIndex(enum displayGlyph glyph) {
                    332:     // These are the only non-ASCII glyphs which always come from the font sheet
                    333:     if (glyph == G_UP_ARROW) return 0x90;
                    334:     if (glyph == G_DOWN_ARROW) return 0x91;
                    335:
                    336:     if (glyph < 128) {
                    337:         // ASCII characters map directly
                    338:         return glyph;
                    339:     } else if (showGraphics && glyph >= 128) {
                    340:         // Tile glyphs have sprite indices starting at 256
                    341:         // -2 to disregard the up and down arrow glyphs
                    342:         return glyph + 128 - 2;
                    343:     } else {
                    344:         unsigned int code = glyphToUnicode(glyph);
                    345:         switch (code) {
                    346:             case U_MIDDLE_DOT: return 0x80;
                    347:             case U_FOUR_DOTS: return 0x81;
                    348:             case U_DIAMOND: return 0x82;
                    349:             case U_FLIPPED_V: return 0x83;
                    350:             case U_ARIES: return 0x84;
                    351:             case U_ESZETT: return 0xdf;
                    352:             case U_ANKH: return 0x85;
                    353:             case U_MUSIC_NOTE: return 0x86;
                    354:             case U_CIRCLE: return 0x87;
                    355:             case U_LIGHTNING_BOLT: return 0x99;
                    356:             case U_FILLED_CIRCLE: return 0x89;
                    357:             case U_NEUTER: return 0x8a;
                    358:             case U_U_ACUTE: return 0xda;
                    359:             case U_CURRENCY: return 0xa4;
                    360:             case U_UP_ARROW: return 0x90;
                    361:             case U_DOWN_ARROW: return 0x91;
                    362:             case U_LEFT_ARROW: return 0x92;
                    363:             case U_RIGHT_ARROW: return 0x93;
                    364:             case U_OMEGA: return 0x96;
                    365:             case U_CIRCLE_BARS: return 0x8c;
                    366:             case U_FILLED_CIRCLE_BARS: return 0x8d;
                    367:
                    368:             default:
                    369:                 brogueAssert(code < 256);
                    370:                 return code;
                    371:         }
                    372:     }
                    373: }
                    374:
                    375:
                    376: static void _plotChar(
                    377:     enum displayGlyph inputChar,
                    378:     short x, short y,
                    379:     short foreRed, short foreGreen, short foreBlue,
                    380:     short backRed, short backGreen, short backBlue
                    381: ) {
                    382:     updateTile(y, x, fontIndex(inputChar),
                    383:         foreRed, foreGreen, foreBlue,
                    384:         backRed, backGreen, backBlue);
                    385: }
                    386:
                    387:
                    388: static void _remap(const char *from, const char *to) {
                    389:     if (nremaps < MAX_REMAPS) {
                    390:         remapping[nremaps].from = from[0];
                    391:         remapping[nremaps].to = to[0];
                    392:         nremaps++;
                    393:     }
                    394: }
                    395:
                    396:
                    397: /*
                    398:  * Take screenshot in current working directory (ScreenshotN.png)
                    399:  */
                    400: static boolean _takeScreenshot() {
                    401:     SDL_Surface *screenshot = captureScreen();
                    402:     if (!screenshot) return false;
                    403:
                    404:     // choose filename
                    405:     char screenshotFilepath[BROGUE_FILENAME_MAX];
                    406:     getAvailableFilePath(screenshotFilepath, "Screenshot", SCREENSHOT_SUFFIX);
                    407:     strcat(screenshotFilepath, SCREENSHOT_SUFFIX);
                    408:
                    409:     // save to PNG
                    410:     IMG_SavePNG(screenshot, screenshotFilepath);
                    411:     SDL_FreeSurface(screenshot);
                    412:
                    413:     return true;
                    414: }
                    415:
                    416:
                    417: static boolean _setGraphicsEnabled(boolean state) {
                    418:     showGraphics = state;
                    419:     refreshScreen();
                    420:     updateScreen();
                    421:     return state;
                    422: }
                    423:
                    424:
                    425: struct brogueConsole sdlConsole = {
                    426:     _gameLoop,
                    427:     _pauseForMilliseconds,
                    428:     _nextKeyOrMouseEvent,
                    429:     _plotChar,
                    430:     _remap,
                    431:     _modifierHeld,
                    432:     NULL,
                    433:     _takeScreenshot,
                    434:     _setGraphicsEnabled
                    435: };

CVSweb