[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     ! 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